2019-02-27 09:31:53 +00:00
# include <limits>
2017-02-26 20:46:33 +00:00
# include <string.h>
# include <map>
# include <string>
2019-05-21 19:12:37 +00:00
# include <expat.h>
2017-02-26 20:46:33 +00:00
2017-08-03 15:31:31 +00:00
# include <boost/nowide/cstdio.hpp>
2017-02-26 20:46:33 +00:00
# include "../libslic3r.h"
# include "../Model.hpp"
2018-02-14 13:30:03 +00:00
# include "../GCode.hpp"
2018-09-25 09:53:05 +00:00
# include "../PrintConfig.hpp"
2018-07-23 12:39:50 +00:00
# include "../Utils.hpp"
2019-08-26 07:35:04 +00:00
# include "../I18N.hpp"
2019-09-19 10:39:59 +00:00
# include "../Geometry.hpp"
2020-01-23 11:49:39 +00:00
# include "../CustomGCode.hpp"
2019-08-26 07:35:04 +00:00
2017-02-26 20:46:33 +00:00
# include "AMF.hpp"
2019-11-11 15:01:34 +00:00
# include <boost/property_tree/ptree.hpp>
# include <boost/property_tree/xml_parser.hpp>
namespace pt = boost : : property_tree ;
2018-02-14 13:30:03 +00:00
# include <boost/filesystem/operations.hpp>
# include <boost/algorithm/string.hpp>
2018-05-24 07:57:12 +00:00
# include <boost/nowide/fstream.hpp>
2019-06-03 13:27:46 +00:00
# include "miniz_extension.hpp"
2018-02-14 13:30:03 +00:00
2017-02-26 20:46:33 +00:00
#if 0
// Enable debugging and assert in this file.
# define DEBUG
# define _DEBUG
# undef NDEBUG
# endif
# include <assert.h>
2018-04-30 10:03:06 +00:00
// VERSION NUMBERS
// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
// 1 : Introduction of amf versioning. No other change in data saved into amf files.
2018-09-20 13:00:40 +00:00
// 2 : Added z component of offset
// Added x and y components of rotation
2018-09-24 13:54:09 +00:00
// Added x, y and z components of scale
2018-10-22 09:45:03 +00:00
// Added x, y and z components of mirror
2019-12-19 09:22:46 +00:00
// 3 : Added volumes' matrices and source data, meshes transformed back to their coordinate system on loading.
2019-12-19 08:41:30 +00:00
// WARNING !! -> the version number has been rolled back to 2
// the next change should use 4
const unsigned int VERSION_AMF = 2 ;
2019-12-19 09:22:46 +00:00
const unsigned int VERSION_AMF_COMPATIBLE = 3 ;
2018-04-30 10:03:06 +00:00
const char * SLIC3RPE_AMF_VERSION = " slic3rpe_amf_version " ;
2018-02-14 13:30:03 +00:00
const char * SLIC3R_CONFIG_TYPE = " slic3rpe_config " ;
2017-02-26 20:46:33 +00:00
namespace Slic3r
{
2019-08-26 07:35:04 +00:00
//! macro used to mark string used at localization,
//! return same string
# define L(s) (s)
# define _(s) Slic3r::I18N::translate(s)
2017-02-26 20:46:33 +00:00
struct AMFParserContext
{
2019-08-23 11:12:31 +00:00
AMFParserContext ( XML_Parser parser , DynamicPrintConfig * config , Model * model ) :
2018-04-30 10:03:06 +00:00
m_version ( 0 ) ,
2017-02-26 20:46:33 +00:00
m_parser ( parser ) ,
m_model ( * model ) ,
m_object ( nullptr ) ,
m_volume ( nullptr ) ,
m_material ( nullptr ) ,
2018-02-14 13:30:03 +00:00
m_instance ( nullptr ) ,
2018-09-25 09:53:05 +00:00
m_config ( config )
2017-02-26 20:46:33 +00:00
{
m_path . reserve ( 12 ) ;
}
void stop ( )
{
XML_StopParser ( m_parser , 0 ) ;
}
void startElement ( const char * name , const char * * atts ) ;
void endElement ( const char * name ) ;
void endDocument ( ) ;
void characters ( const XML_Char * s , int len ) ;
static void XMLCALL startElement ( void * userData , const char * name , const char * * atts )
{
AMFParserContext * ctx = ( AMFParserContext * ) userData ;
ctx - > startElement ( name , atts ) ;
}
static void XMLCALL endElement ( void * userData , const char * name )
{
AMFParserContext * ctx = ( AMFParserContext * ) userData ;
ctx - > endElement ( name ) ;
}
/* s is not 0 terminated. */
static void XMLCALL characters ( void * userData , const XML_Char * s , int len )
{
AMFParserContext * ctx = ( AMFParserContext * ) userData ;
ctx - > characters ( s , len ) ;
}
static const char * get_attribute ( const char * * atts , const char * id ) {
if ( atts = = nullptr )
return nullptr ;
while ( * atts ! = nullptr ) {
if ( strcmp ( * ( atts + + ) , id ) = = 0 )
return * atts ;
+ + atts ;
}
return nullptr ;
}
enum AMFNodeType {
NODE_TYPE_INVALID = 0 ,
NODE_TYPE_UNKNOWN ,
NODE_TYPE_AMF , // amf
// amf/metadata
NODE_TYPE_MATERIAL , // amf/material
// amf/material/metadata
NODE_TYPE_OBJECT , // amf/object
// amf/object/metadata
2019-06-11 07:50:58 +00:00
NODE_TYPE_LAYER_CONFIG , // amf/object/layer_config_ranges
NODE_TYPE_RANGE , // amf/object/layer_config_ranges/range
// amf/object/layer_config_ranges/range/metadata
2017-02-26 20:46:33 +00:00
NODE_TYPE_MESH , // amf/object/mesh
NODE_TYPE_VERTICES , // amf/object/mesh/vertices
NODE_TYPE_VERTEX , // amf/object/mesh/vertices/vertex
NODE_TYPE_COORDINATES , // amf/object/mesh/vertices/vertex/coordinates
NODE_TYPE_COORDINATE_X , // amf/object/mesh/vertices/vertex/coordinates/x
NODE_TYPE_COORDINATE_Y , // amf/object/mesh/vertices/vertex/coordinates/y
NODE_TYPE_COORDINATE_Z , // amf/object/mesh/vertices/vertex/coordinates/z
NODE_TYPE_VOLUME , // amf/object/mesh/volume
// amf/object/mesh/volume/metadata
NODE_TYPE_TRIANGLE , // amf/object/mesh/volume/triangle
NODE_TYPE_VERTEX1 , // amf/object/mesh/volume/triangle/v1
NODE_TYPE_VERTEX2 , // amf/object/mesh/volume/triangle/v2
NODE_TYPE_VERTEX3 , // amf/object/mesh/volume/triangle/v3
NODE_TYPE_CONSTELLATION , // amf/constellation
NODE_TYPE_INSTANCE , // amf/constellation/instance
NODE_TYPE_DELTAX , // amf/constellation/instance/deltax
NODE_TYPE_DELTAY , // amf/constellation/instance/deltay
2018-09-17 13:12:13 +00:00
NODE_TYPE_DELTAZ , // amf/constellation/instance/deltaz
2018-09-20 13:00:40 +00:00
NODE_TYPE_RX , // amf/constellation/instance/rx
NODE_TYPE_RY , // amf/constellation/instance/ry
2017-02-26 20:46:33 +00:00
NODE_TYPE_RZ , // amf/constellation/instance/rz
2017-03-06 16:35:38 +00:00
NODE_TYPE_SCALE , // amf/constellation/instance/scale
2018-10-22 09:45:03 +00:00
NODE_TYPE_SCALEX , // amf/constellation/instance/scalex
NODE_TYPE_SCALEY , // amf/constellation/instance/scaley
NODE_TYPE_SCALEZ , // amf/constellation/instance/scalez
NODE_TYPE_MIRRORX , // amf/constellation/instance/mirrorx
NODE_TYPE_MIRRORY , // amf/constellation/instance/mirrory
NODE_TYPE_MIRRORZ , // amf/constellation/instance/mirrorz
2019-08-06 12:54:38 +00:00
NODE_TYPE_PRINTABLE , // amf/constellation/instance/mirrorz
2019-11-11 15:01:34 +00:00
NODE_TYPE_CUSTOM_GCODE , // amf/custom_code_per_height
NODE_TYPE_GCODE_PER_HEIGHT , // amf/custom_code_per_height/code
2020-01-23 11:49:39 +00:00
NODE_TYPE_CUSTOM_GCODE_MODE , // amf/custom_code_per_height/mode
2017-02-26 20:46:33 +00:00
NODE_TYPE_METADATA , // anywhere under amf/*/metadata
} ;
struct Instance {
2018-10-22 09:45:03 +00:00
Instance ( )
: deltax_set ( false ) , deltay_set ( false ) , deltaz_set ( false )
, rx_set ( false ) , ry_set ( false ) , rz_set ( false )
, scalex_set ( false ) , scaley_set ( false ) , scalez_set ( false )
2019-08-06 12:54:38 +00:00
, mirrorx_set ( false ) , mirrory_set ( false ) , mirrorz_set ( false )
, printable ( true ) { }
2017-02-26 20:46:33 +00:00
// Shift in the X axis.
float deltax ;
bool deltax_set ;
// Shift in the Y axis.
float deltay ;
bool deltay_set ;
2018-09-17 13:12:13 +00:00
// Shift in the Z axis.
float deltaz ;
bool deltaz_set ;
2018-09-20 13:00:40 +00:00
// Rotation around the X axis.
float rx ;
bool rx_set ;
// Rotation around the Y axis.
float ry ;
bool ry_set ;
2017-02-26 20:46:33 +00:00
// Rotation around the Z axis.
float rz ;
bool rz_set ;
2018-09-24 13:54:09 +00:00
// Scaling factors
float scalex ;
bool scalex_set ;
float scaley ;
bool scaley_set ;
float scalez ;
bool scalez_set ;
2018-10-22 09:45:03 +00:00
// Mirroring factors
float mirrorx ;
bool mirrorx_set ;
float mirrory ;
bool mirrory_set ;
float mirrorz ;
bool mirrorz_set ;
2019-08-06 12:54:38 +00:00
// printable property
bool printable ;
2019-03-25 11:07:43 +00:00
bool anything_set ( ) const { return deltax_set | | deltay_set | | deltaz_set | |
rx_set | | ry_set | | rz_set | |
scalex_set | | scaley_set | | scalez_set | |
mirrorx_set | | mirrory_set | | mirrorz_set ; }
2017-02-26 20:46:33 +00:00
} ;
struct Object {
Object ( ) : idx ( - 1 ) { }
int idx ;
std : : vector < Instance > instances ;
} ;
2018-04-30 10:03:06 +00:00
// Version of the amf file
2019-06-20 14:15:09 +00:00
unsigned int m_version ;
2017-02-26 20:46:33 +00:00
// Current Expat XML parser instance.
XML_Parser m_parser ;
// Model to receive objects extracted from an AMF file.
Model & m_model ;
// Current parsing path in the XML file.
std : : vector < AMFNodeType > m_path ;
// Current object allocated for an amf/object XML subtree.
ModelObject * m_object ;
// Map from obect name to object idx & instances.
std : : map < std : : string , Object > m_object_instances_map ;
// Vertices parsed for the current m_object.
std : : vector < float > m_object_vertices ;
// Current volume allocated for an amf/object/mesh/volume subtree.
ModelVolume * m_volume ;
// Faces collected for the current m_volume.
std : : vector < int > m_volume_facets ;
2019-12-19 09:22:46 +00:00
// Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system.
Transform3d m_volume_transform ;
2017-02-26 20:46:33 +00:00
// Current material allocated for an amf/metadata subtree.
ModelMaterial * m_material ;
// Current instance allocated for an amf/constellation/instance subtree.
Instance * m_instance ;
// Generic string buffer for vertices, face indices, metadata etc.
2019-11-11 15:01:34 +00:00
std : : string m_value [ 4 ] ;
2018-09-25 09:53:05 +00:00
// Pointer to config to update if config data are stored inside the amf file
DynamicPrintConfig * m_config ;
2017-05-03 16:28:22 +00:00
private :
AMFParserContext & operator = ( AMFParserContext & ) ;
2017-02-26 20:46:33 +00:00
} ;
void AMFParserContext : : startElement ( const char * name , const char * * atts )
{
AMFNodeType node_type_new = NODE_TYPE_UNKNOWN ;
switch ( m_path . size ( ) ) {
case 0 :
// An AMF file must start with an <amf> tag.
node_type_new = NODE_TYPE_AMF ;
if ( strcmp ( name , " amf " ) ! = 0 )
this - > stop ( ) ;
break ;
case 1 :
if ( strcmp ( name , " metadata " ) = = 0 ) {
const char * type = get_attribute ( atts , " type " ) ;
if ( type ! = nullptr ) {
m_value [ 0 ] = type ;
node_type_new = NODE_TYPE_METADATA ;
}
} else if ( strcmp ( name , " material " ) = = 0 ) {
const char * material_id = get_attribute ( atts , " id " ) ;
m_material = m_model . add_material ( ( material_id = = nullptr ) ? " _ " : material_id ) ;
node_type_new = NODE_TYPE_MATERIAL ;
} else if ( strcmp ( name , " object " ) = = 0 ) {
const char * object_id = get_attribute ( atts , " id " ) ;
if ( object_id = = nullptr )
this - > stop ( ) ;
else {
assert ( m_object_vertices . empty ( ) ) ;
m_object = m_model . add_object ( ) ;
m_object_instances_map [ object_id ] . idx = int ( m_model . objects . size ( ) ) - 1 ;
node_type_new = NODE_TYPE_OBJECT ;
}
} else if ( strcmp ( name , " constellation " ) = = 0 ) {
node_type_new = NODE_TYPE_CONSTELLATION ;
2019-11-11 15:01:34 +00:00
} else if ( strcmp ( name , " custom_gcodes_per_height " ) = = 0 ) {
node_type_new = NODE_TYPE_CUSTOM_GCODE ;
2017-02-26 20:46:33 +00:00
}
break ;
case 2 :
if ( strcmp ( name , " metadata " ) = = 0 ) {
if ( m_path [ 1 ] = = NODE_TYPE_MATERIAL | | m_path [ 1 ] = = NODE_TYPE_OBJECT ) {
m_value [ 0 ] = get_attribute ( atts , " type " ) ;
node_type_new = NODE_TYPE_METADATA ;
}
2019-06-11 07:50:58 +00:00
} else if ( strcmp ( name , " layer_config_ranges " ) = = 0 & & m_path [ 1 ] = = NODE_TYPE_OBJECT )
node_type_new = NODE_TYPE_LAYER_CONFIG ;
else if ( strcmp ( name , " mesh " ) = = 0 ) {
2017-02-26 20:46:33 +00:00
if ( m_path [ 1 ] = = NODE_TYPE_OBJECT )
node_type_new = NODE_TYPE_MESH ;
} else if ( strcmp ( name , " instance " ) = = 0 ) {
if ( m_path [ 1 ] = = NODE_TYPE_CONSTELLATION ) {
const char * object_id = get_attribute ( atts , " objectid " ) ;
if ( object_id = = nullptr )
this - > stop ( ) ;
else {
m_object_instances_map [ object_id ] . instances . push_back ( AMFParserContext : : Instance ( ) ) ;
m_instance = & m_object_instances_map [ object_id ] . instances . back ( ) ;
node_type_new = NODE_TYPE_INSTANCE ;
}
}
else
this - > stop ( ) ;
2019-11-11 15:01:34 +00:00
}
2020-01-23 11:49:39 +00:00
else if ( m_path [ 1 ] = = NODE_TYPE_CUSTOM_GCODE ) {
if ( strcmp ( name , " code " ) = = 0 ) {
node_type_new = NODE_TYPE_GCODE_PER_HEIGHT ;
m_value [ 0 ] = get_attribute ( atts , " print_z " ) ;
m_value [ 1 ] = get_attribute ( atts , " gcode " ) ;
m_value [ 2 ] = get_attribute ( atts , " extruder " ) ;
m_value [ 3 ] = get_attribute ( atts , " color " ) ;
}
else if ( strcmp ( name , " mode " ) = = 0 ) {
node_type_new = NODE_TYPE_CUSTOM_GCODE_MODE ;
m_value [ 0 ] = get_attribute ( atts , " value " ) ;
}
2017-02-26 20:46:33 +00:00
}
break ;
case 3 :
if ( m_path [ 2 ] = = NODE_TYPE_MESH ) {
assert ( m_object ) ;
if ( strcmp ( name , " vertices " ) = = 0 )
node_type_new = NODE_TYPE_VERTICES ;
else if ( strcmp ( name , " volume " ) = = 0 ) {
assert ( ! m_volume ) ;
2019-01-18 11:52:09 +00:00
m_volume = m_object - > add_volume ( TriangleMesh ( ) ) ;
2019-12-19 09:22:46 +00:00
m_volume_transform = Transform3d : : Identity ( ) ;
2019-01-18 11:52:09 +00:00
node_type_new = NODE_TYPE_VOLUME ;
2017-02-26 20:46:33 +00:00
}
} else if ( m_path [ 2 ] = = NODE_TYPE_INSTANCE ) {
assert ( m_instance ) ;
if ( strcmp ( name , " deltax " ) = = 0 )
node_type_new = NODE_TYPE_DELTAX ;
else if ( strcmp ( name , " deltay " ) = = 0 )
node_type_new = NODE_TYPE_DELTAY ;
2018-09-17 13:12:13 +00:00
else if ( strcmp ( name , " deltaz " ) = = 0 )
node_type_new = NODE_TYPE_DELTAZ ;
2018-09-20 13:00:40 +00:00
else if ( strcmp ( name , " rx " ) = = 0 )
node_type_new = NODE_TYPE_RX ;
else if ( strcmp ( name , " ry " ) = = 0 )
node_type_new = NODE_TYPE_RY ;
2017-02-26 20:46:33 +00:00
else if ( strcmp ( name , " rz " ) = = 0 )
node_type_new = NODE_TYPE_RZ ;
2018-09-24 13:54:09 +00:00
else if ( strcmp ( name , " scalex " ) = = 0 )
node_type_new = NODE_TYPE_SCALEX ;
else if ( strcmp ( name , " scaley " ) = = 0 )
node_type_new = NODE_TYPE_SCALEY ;
else if ( strcmp ( name , " scalez " ) = = 0 )
node_type_new = NODE_TYPE_SCALEZ ;
2017-03-06 16:35:38 +00:00
else if ( strcmp ( name , " scale " ) = = 0 )
node_type_new = NODE_TYPE_SCALE ;
2018-10-22 09:45:03 +00:00
else if ( strcmp ( name , " mirrorx " ) = = 0 )
node_type_new = NODE_TYPE_MIRRORX ;
else if ( strcmp ( name , " mirrory " ) = = 0 )
node_type_new = NODE_TYPE_MIRRORY ;
else if ( strcmp ( name , " mirrorz " ) = = 0 )
node_type_new = NODE_TYPE_MIRRORZ ;
2019-08-06 12:54:38 +00:00
else if ( strcmp ( name , " printable " ) = = 0 )
node_type_new = NODE_TYPE_PRINTABLE ;
2017-02-26 20:46:33 +00:00
}
2019-06-11 07:50:58 +00:00
else if ( m_path [ 2 ] = = NODE_TYPE_LAYER_CONFIG & & strcmp ( name , " range " ) = = 0 ) {
assert ( m_object ) ;
node_type_new = NODE_TYPE_RANGE ;
}
2017-02-26 20:46:33 +00:00
break ;
case 4 :
if ( m_path [ 3 ] = = NODE_TYPE_VERTICES ) {
if ( strcmp ( name , " vertex " ) = = 0 )
node_type_new = NODE_TYPE_VERTEX ;
} else if ( m_path [ 3 ] = = NODE_TYPE_VOLUME ) {
if ( strcmp ( name , " metadata " ) = = 0 ) {
const char * type = get_attribute ( atts , " type " ) ;
if ( type = = nullptr )
this - > stop ( ) ;
else {
m_value [ 0 ] = type ;
node_type_new = NODE_TYPE_METADATA ;
}
} else if ( strcmp ( name , " triangle " ) = = 0 )
node_type_new = NODE_TYPE_TRIANGLE ;
}
2019-06-11 07:50:58 +00:00
else if ( m_path [ 3 ] = = NODE_TYPE_RANGE & & strcmp ( name , " metadata " ) = = 0 ) {
m_value [ 0 ] = get_attribute ( atts , " type " ) ;
node_type_new = NODE_TYPE_METADATA ;
}
2017-02-26 20:46:33 +00:00
break ;
case 5 :
if ( strcmp ( name , " coordinates " ) = = 0 ) {
if ( m_path [ 4 ] = = NODE_TYPE_VERTEX ) {
node_type_new = NODE_TYPE_COORDINATES ;
} else
this - > stop ( ) ;
} else if ( name [ 0 ] = = ' v ' & & name [ 1 ] > = ' 1 ' & & name [ 1 ] < = ' 3 ' & & name [ 2 ] = = 0 ) {
if ( m_path [ 4 ] = = NODE_TYPE_TRIANGLE ) {
node_type_new = AMFNodeType ( NODE_TYPE_VERTEX1 + name [ 1 ] - ' 1 ' ) ;
} else
this - > stop ( ) ;
}
break ;
case 6 :
if ( ( name [ 0 ] = = ' x ' | | name [ 0 ] = = ' y ' | | name [ 0 ] = = ' z ' ) & & name [ 1 ] = = 0 ) {
if ( m_path [ 5 ] = = NODE_TYPE_COORDINATES )
node_type_new = AMFNodeType ( NODE_TYPE_COORDINATE_X + name [ 0 ] - ' x ' ) ;
else
this - > stop ( ) ;
}
break ;
default :
break ;
}
m_path . push_back ( node_type_new ) ;
}
void AMFParserContext : : characters ( const XML_Char * s , int len )
{
if ( m_path . back ( ) = = NODE_TYPE_METADATA ) {
m_value [ 1 ] . append ( s , len ) ;
}
else
{
switch ( m_path . size ( ) ) {
case 4 :
2018-10-22 09:45:03 +00:00
if ( m_path . back ( ) = = NODE_TYPE_DELTAX | |
m_path . back ( ) = = NODE_TYPE_DELTAY | |
m_path . back ( ) = = NODE_TYPE_DELTAZ | |
2018-09-20 13:00:40 +00:00
m_path . back ( ) = = NODE_TYPE_RX | |
m_path . back ( ) = = NODE_TYPE_RY | |
m_path . back ( ) = = NODE_TYPE_RZ | |
2018-09-24 13:54:09 +00:00
m_path . back ( ) = = NODE_TYPE_SCALEX | |
m_path . back ( ) = = NODE_TYPE_SCALEY | |
m_path . back ( ) = = NODE_TYPE_SCALEZ | |
2018-10-22 09:45:03 +00:00
m_path . back ( ) = = NODE_TYPE_SCALE | |
m_path . back ( ) = = NODE_TYPE_MIRRORX | |
m_path . back ( ) = = NODE_TYPE_MIRRORY | |
2019-08-06 12:54:38 +00:00
m_path . back ( ) = = NODE_TYPE_MIRRORZ | |
m_path . back ( ) = = NODE_TYPE_PRINTABLE )
2017-02-26 20:46:33 +00:00
m_value [ 0 ] . append ( s , len ) ;
break ;
case 6 :
switch ( m_path . back ( ) ) {
case NODE_TYPE_VERTEX1 : m_value [ 0 ] . append ( s , len ) ; break ;
case NODE_TYPE_VERTEX2 : m_value [ 1 ] . append ( s , len ) ; break ;
case NODE_TYPE_VERTEX3 : m_value [ 2 ] . append ( s , len ) ; break ;
default : break ;
}
case 7 :
switch ( m_path . back ( ) ) {
case NODE_TYPE_COORDINATE_X : m_value [ 0 ] . append ( s , len ) ; break ;
case NODE_TYPE_COORDINATE_Y : m_value [ 1 ] . append ( s , len ) ; break ;
case NODE_TYPE_COORDINATE_Z : m_value [ 2 ] . append ( s , len ) ; break ;
default : break ;
}
default :
break ;
}
}
}
2017-05-03 16:28:22 +00:00
void AMFParserContext : : endElement ( const char * /* name */ )
2017-02-26 20:46:33 +00:00
{
switch ( m_path . back ( ) ) {
// Constellation transformation:
case NODE_TYPE_DELTAX :
assert ( m_instance ) ;
m_instance - > deltax = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > deltax_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
case NODE_TYPE_DELTAY :
assert ( m_instance ) ;
m_instance - > deltay = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > deltay_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
2018-09-17 13:12:13 +00:00
case NODE_TYPE_DELTAZ :
assert ( m_instance ) ;
m_instance - > deltaz = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > deltaz_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
2018-09-20 13:00:40 +00:00
case NODE_TYPE_RX :
assert ( m_instance ) ;
m_instance - > rx = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > rx_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
case NODE_TYPE_RY :
assert ( m_instance ) ;
m_instance - > ry = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > ry_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
2017-02-26 20:46:33 +00:00
case NODE_TYPE_RZ :
assert ( m_instance ) ;
m_instance - > rz = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > rz_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
2017-03-06 16:35:38 +00:00
case NODE_TYPE_SCALE :
assert ( m_instance ) ;
2018-09-24 13:54:09 +00:00
m_instance - > scalex = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > scalex_set = true ;
m_instance - > scaley = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > scaley_set = true ;
m_instance - > scalez = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > scalez_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
case NODE_TYPE_SCALEX :
assert ( m_instance ) ;
m_instance - > scalex = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > scalex_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
case NODE_TYPE_SCALEY :
assert ( m_instance ) ;
m_instance - > scaley = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > scaley_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
case NODE_TYPE_SCALEZ :
assert ( m_instance ) ;
m_instance - > scalez = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > scalez_set = true ;
2017-03-06 16:35:38 +00:00
m_value [ 0 ] . clear ( ) ;
break ;
2018-10-22 09:45:03 +00:00
case NODE_TYPE_MIRRORX :
assert ( m_instance ) ;
m_instance - > mirrorx = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > mirrorx_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
case NODE_TYPE_MIRRORY :
assert ( m_instance ) ;
m_instance - > mirrory = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > mirrory_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
case NODE_TYPE_MIRRORZ :
assert ( m_instance ) ;
m_instance - > mirrorz = float ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_instance - > mirrorz_set = true ;
m_value [ 0 ] . clear ( ) ;
break ;
2019-08-06 12:54:38 +00:00
case NODE_TYPE_PRINTABLE :
assert ( m_instance ) ;
m_instance - > printable = bool ( atoi ( m_value [ 0 ] . c_str ( ) ) ) ;
m_value [ 0 ] . clear ( ) ;
break ;
2017-02-26 20:46:33 +00:00
// Object vertices:
case NODE_TYPE_VERTEX :
assert ( m_object ) ;
// Parse the vertex data
2018-04-30 10:03:06 +00:00
m_object_vertices . emplace_back ( ( float ) atof ( m_value [ 0 ] . c_str ( ) ) ) ;
m_object_vertices . emplace_back ( ( float ) atof ( m_value [ 1 ] . c_str ( ) ) ) ;
m_object_vertices . emplace_back ( ( float ) atof ( m_value [ 2 ] . c_str ( ) ) ) ;
2017-02-26 20:46:33 +00:00
m_value [ 0 ] . clear ( ) ;
m_value [ 1 ] . clear ( ) ;
m_value [ 2 ] . clear ( ) ;
break ;
// Faces of the current volume:
case NODE_TYPE_TRIANGLE :
assert ( m_object & & m_volume ) ;
m_volume_facets . push_back ( atoi ( m_value [ 0 ] . c_str ( ) ) ) ;
m_volume_facets . push_back ( atoi ( m_value [ 1 ] . c_str ( ) ) ) ;
m_volume_facets . push_back ( atoi ( m_value [ 2 ] . c_str ( ) ) ) ;
m_value [ 0 ] . clear ( ) ;
m_value [ 1 ] . clear ( ) ;
m_value [ 2 ] . clear ( ) ;
break ;
// Closing the current volume. Create an STL from m_volume_facets pointing to m_object_vertices.
case NODE_TYPE_VOLUME :
{
assert ( m_object & & m_volume ) ;
2019-06-11 15:08:47 +00:00
TriangleMesh mesh ;
stl_file & stl = mesh . stl ;
2017-02-26 20:46:33 +00:00
stl . stats . type = inmemory ;
stl . stats . number_of_facets = int ( m_volume_facets . size ( ) / 3 ) ;
stl . stats . original_num_facets = stl . stats . number_of_facets ;
stl_allocate ( & stl ) ;
2019-09-23 11:54:49 +00:00
2019-12-19 09:22:46 +00:00
bool has_transform = ! m_volume_transform . isApprox ( Transform3d : : Identity ( ) , 1e-10 ) ;
2017-02-26 20:46:33 +00:00
for ( size_t i = 0 ; i < m_volume_facets . size ( ) ; ) {
stl_facet & facet = stl . facet_start [ i / 3 ] ;
2019-09-23 11:54:49 +00:00
for ( unsigned int v = 0 ; v < 3 ; + + v )
{
unsigned int tri_id = m_volume_facets [ i + + ] * 3 ;
2019-12-20 11:11:58 +00:00
facet . vertex [ v ] = Vec3f ( m_object_vertices [ tri_id + 0 ] , m_object_vertices [ tri_id + 1 ] , m_object_vertices [ tri_id + 2 ] ) ;
2019-09-23 11:54:49 +00:00
}
2019-12-19 09:22:46 +00:00
}
2017-02-26 20:46:33 +00:00
stl_get_size ( & stl ) ;
2019-06-11 15:08:47 +00:00
mesh . repair ( ) ;
m_volume - > set_mesh ( std : : move ( mesh ) ) ;
2019-12-20 11:11:58 +00:00
// stores the volume matrix taken from the metadata, if present
if ( has_transform )
m_volume - > source . transform = Slic3r : : Geometry : : Transformation ( m_volume_transform ) ;
2019-10-21 06:48:38 +00:00
if ( m_volume - > source . input_file . empty ( ) & & ( m_volume - > type ( ) = = ModelVolumeType : : MODEL_PART ) )
{
m_volume - > source . object_idx = ( int ) m_model . objects . size ( ) - 1 ;
m_volume - > source . volume_idx = ( int ) m_model . objects . back ( ) - > volumes . size ( ) - 1 ;
m_volume - > center_geometry_after_creation ( ) ;
}
else
// pass false if the mesh offset has been already taken from the data
m_volume - > center_geometry_after_creation ( m_volume - > source . input_file . empty ( ) ) ;
2018-08-15 10:50:06 +00:00
m_volume - > calculate_convex_hull ( ) ;
2017-02-26 20:46:33 +00:00
m_volume_facets . clear ( ) ;
m_volume = nullptr ;
break ;
}
case NODE_TYPE_OBJECT :
assert ( m_object ) ;
m_object_vertices . clear ( ) ;
m_object = nullptr ;
break ;
case NODE_TYPE_MATERIAL :
assert ( m_material ) ;
m_material = nullptr ;
break ;
case NODE_TYPE_INSTANCE :
assert ( m_instance ) ;
m_instance = nullptr ;
break ;
2019-11-11 15:01:34 +00:00
case NODE_TYPE_GCODE_PER_HEIGHT : {
2020-01-23 11:49:39 +00:00
double print_z = double ( atof ( m_value [ 0 ] . c_str ( ) ) ) ;
2019-11-11 15:01:34 +00:00
const std : : string & gcode = m_value [ 1 ] ;
int extruder = atoi ( m_value [ 2 ] . c_str ( ) ) ;
const std : : string & color = m_value [ 3 ] ;
2020-01-23 11:49:39 +00:00
m_model . custom_gcode_per_print_z . gcodes . push_back ( CustomGCode : : Item { print_z , gcode , extruder , color } ) ;
2019-11-11 15:01:34 +00:00
for ( std : : string & val : m_value )
val . clear ( ) ;
break ;
}
2020-01-23 11:49:39 +00:00
case NODE_TYPE_CUSTOM_GCODE_MODE : {
const std : : string & mode = m_value [ 0 ] ;
m_model . custom_gcode_per_print_z . mode = mode = = CustomGCode : : SingleExtruderMode ? CustomGCode : : Mode : : SingleExtruder :
mode = = CustomGCode : : MultiAsSingleMode ? CustomGCode : : Mode : : MultiAsSingle :
CustomGCode : : Mode : : MultiExtruder ;
for ( std : : string & val : m_value )
val . clear ( ) ;
break ;
}
2017-02-26 20:46:33 +00:00
case NODE_TYPE_METADATA :
2018-09-25 09:53:05 +00:00
if ( ( m_config ! = nullptr ) & & strncmp ( m_value [ 0 ] . c_str ( ) , SLIC3R_CONFIG_TYPE , strlen ( SLIC3R_CONFIG_TYPE ) ) = = 0 )
m_config - > load_from_gcode_string ( m_value [ 1 ] . c_str ( ) ) ;
2018-02-14 13:30:03 +00:00
else if ( strncmp ( m_value [ 0 ] . c_str ( ) , " slic3r. " , 7 ) = = 0 ) {
2017-02-26 20:46:33 +00:00
const char * opt_key = m_value [ 0 ] . c_str ( ) + 7 ;
if ( print_config_def . options . find ( opt_key ) ! = print_config_def . options . end ( ) ) {
DynamicPrintConfig * config = nullptr ;
if ( m_path . size ( ) = = 3 ) {
if ( m_path [ 1 ] = = NODE_TYPE_MATERIAL & & m_material )
config = & m_material - > config ;
else if ( m_path [ 1 ] = = NODE_TYPE_OBJECT & & m_object )
config = & m_object - > config ;
2019-06-11 07:50:58 +00:00
}
else if ( m_path . size ( ) = = 5 & & m_path [ 3 ] = = NODE_TYPE_VOLUME & & m_volume )
2017-02-26 20:46:33 +00:00
config = & m_volume - > config ;
2019-06-11 07:50:58 +00:00
else if ( m_path . size ( ) = = 5 & & m_path [ 3 ] = = NODE_TYPE_RANGE & & m_object & & ! m_object - > layer_config_ranges . empty ( ) ) {
auto it = - - m_object - > layer_config_ranges . end ( ) ;
config = & it - > second ;
}
2017-02-26 20:46:33 +00:00
if ( config )
config - > set_deserialize ( opt_key , m_value [ 1 ] ) ;
} else if ( m_path . size ( ) = = 3 & & m_path [ 1 ] = = NODE_TYPE_OBJECT & & m_object & & strcmp ( opt_key , " layer_height_profile " ) = = 0 ) {
// Parse object's layer height profile, a semicolon separated list of floats.
2020-01-03 15:32:56 +00:00
char * p = m_value [ 1 ] . data ( ) ;
2017-02-26 20:46:33 +00:00
for ( ; ; ) {
char * end = strchr ( p , ' ; ' ) ;
if ( end ! = nullptr )
* end = 0 ;
m_object - > layer_height_profile . push_back ( float ( atof ( p ) ) ) ;
if ( end = = nullptr )
break ;
p = end + 1 ;
}
2018-10-03 13:13:54 +00:00
}
else if ( m_path . size ( ) = = 3 & & m_path [ 1 ] = = NODE_TYPE_OBJECT & & m_object & & strcmp ( opt_key , " sla_support_points " ) = = 0 ) {
// Parse object's layer height profile, a semicolon separated list of floats.
unsigned char coord_idx = 0 ;
2019-01-30 07:26:23 +00:00
Eigen : : Matrix < float , 5 , 1 , Eigen : : DontAlign > point ( Eigen : : Matrix < float , 5 , 1 , Eigen : : DontAlign > : : Zero ( ) ) ;
2020-01-03 15:32:56 +00:00
char * p = m_value [ 1 ] . data ( ) ;
2018-10-03 13:13:54 +00:00
for ( ; ; ) {
char * end = strchr ( p , ' ; ' ) ;
if ( end ! = nullptr )
* end = 0 ;
2019-06-20 14:15:09 +00:00
point ( coord_idx ) = float ( atof ( p ) ) ;
2019-01-30 07:26:23 +00:00
if ( + + coord_idx = = 5 ) {
m_object - > sla_support_points . push_back ( sla : : SupportPoint ( point ) ) ;
2018-10-03 13:13:54 +00:00
coord_idx = 0 ;
}
if ( end = = nullptr )
break ;
p = end + 1 ;
}
2019-03-11 10:13:37 +00:00
m_object - > sla_points_status = sla : : PointsStatus : : UserModified ;
2018-10-03 13:13:54 +00:00
}
2019-06-11 07:50:58 +00:00
else if ( m_path . size ( ) = = 5 & & m_path [ 1 ] = = NODE_TYPE_OBJECT & & m_path [ 3 ] = = NODE_TYPE_RANGE & &
2019-06-20 14:15:09 +00:00
m_object & & strcmp ( opt_key , " layer_height_range " ) = = 0 ) {
// Parse object's layer_height_range, a semicolon separated doubles.
2020-01-03 15:32:56 +00:00
char * p = m_value [ 1 ] . data ( ) ;
2019-06-11 07:50:58 +00:00
char * end = strchr ( p , ' ; ' ) ;
* end = 0 ;
const t_layer_height_range range = { double ( atof ( p ) ) , double ( atof ( end + 1 ) ) } ;
m_object - > layer_config_ranges [ range ] ;
}
2018-10-03 13:13:54 +00:00
else if ( m_path . size ( ) = = 5 & & m_path [ 3 ] = = NODE_TYPE_VOLUME & & m_volume ) {
2018-09-17 13:12:13 +00:00
if ( strcmp ( opt_key , " modifier " ) = = 0 ) {
// Is this volume a modifier volume?
// "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
2019-02-22 11:12:10 +00:00
m_volume - > set_type ( ( atoi ( m_value [ 1 ] . c_str ( ) ) = = 1 ) ? ModelVolumeType : : PARAMETER_MODIFIER : ModelVolumeType : : MODEL_PART ) ;
2018-09-17 13:12:13 +00:00
} else if ( strcmp ( opt_key , " volume_type " ) = = 0 ) {
m_volume - > set_type ( ModelVolume : : type_from_string ( m_value [ 1 ] ) ) ;
}
2019-09-19 10:39:59 +00:00
else if ( strcmp ( opt_key , " matrix " ) = = 0 ) {
2019-12-19 09:22:46 +00:00
m_volume_transform = Slic3r : : Geometry : : transform3d_from_string ( m_value [ 1 ] ) ;
2019-09-19 10:39:59 +00:00
}
else if ( strcmp ( opt_key , " source_file " ) = = 0 ) {
m_volume - > source . input_file = m_value [ 1 ] ;
}
else if ( strcmp ( opt_key , " source_object_id " ) = = 0 ) {
m_volume - > source . object_idx = : : atoi ( m_value [ 1 ] . c_str ( ) ) ;
}
else if ( strcmp ( opt_key , " source_volume_id " ) = = 0 ) {
m_volume - > source . volume_idx = : : atoi ( m_value [ 1 ] . c_str ( ) ) ;
}
else if ( strcmp ( opt_key , " source_offset_x " ) = = 0 ) {
m_volume - > source . mesh_offset ( 0 ) = : : atof ( m_value [ 1 ] . c_str ( ) ) ;
}
else if ( strcmp ( opt_key , " source_offset_y " ) = = 0 ) {
m_volume - > source . mesh_offset ( 1 ) = : : atof ( m_value [ 1 ] . c_str ( ) ) ;
}
else if ( strcmp ( opt_key , " source_offset_z " ) = = 0 ) {
m_volume - > source . mesh_offset ( 2 ) = : : atof ( m_value [ 1 ] . c_str ( ) ) ;
}
2017-02-26 20:46:33 +00:00
}
} else if ( m_path . size ( ) = = 3 ) {
if ( m_path [ 1 ] = = NODE_TYPE_MATERIAL ) {
if ( m_material )
m_material - > attributes [ m_value [ 0 ] ] = m_value [ 1 ] ;
} else if ( m_path [ 1 ] = = NODE_TYPE_OBJECT ) {
if ( m_object & & m_value [ 0 ] = = " name " )
m_object - > name = std : : move ( m_value [ 1 ] ) ;
}
} else if ( m_path . size ( ) = = 5 & & m_path [ 3 ] = = NODE_TYPE_VOLUME ) {
if ( m_volume & & m_value [ 0 ] = = " name " )
m_volume - > name = std : : move ( m_value [ 1 ] ) ;
}
2018-04-30 10:03:06 +00:00
else if ( strncmp ( m_value [ 0 ] . c_str ( ) , SLIC3RPE_AMF_VERSION , strlen ( SLIC3RPE_AMF_VERSION ) ) = = 0 ) {
m_version = ( unsigned int ) atoi ( m_value [ 1 ] . c_str ( ) ) ;
}
2017-02-26 20:46:33 +00:00
m_value [ 0 ] . clear ( ) ;
m_value [ 1 ] . clear ( ) ;
break ;
default :
break ;
}
m_path . pop_back ( ) ;
}
void AMFParserContext : : endDocument ( )
{
for ( const auto & object : m_object_instances_map ) {
if ( object . second . idx = = - 1 ) {
printf ( " Undefined object %s referenced in constellation \n " , object . first . c_str ( ) ) ;
continue ;
}
for ( const Instance & instance : object . second . instances )
2019-03-25 11:07:43 +00:00
if ( instance . anything_set ( ) ) {
2017-02-26 20:46:33 +00:00
ModelInstance * mi = m_model . objects [ object . second . idx ] - > add_instance ( ) ;
2018-09-20 13:00:40 +00:00
mi - > set_offset ( Vec3d ( instance . deltax_set ? ( double ) instance . deltax : 0.0 , instance . deltay_set ? ( double ) instance . deltay : 0.0 , instance . deltaz_set ? ( double ) instance . deltaz : 0.0 ) ) ;
2018-09-25 08:42:11 +00:00
mi - > set_rotation ( Vec3d ( instance . rx_set ? ( double ) instance . rx : 0.0 , instance . ry_set ? ( double ) instance . ry : 0.0 , instance . rz_set ? ( double ) instance . rz : 0.0 ) ) ;
mi - > set_scaling_factor ( Vec3d ( instance . scalex_set ? ( double ) instance . scalex : 1.0 , instance . scaley_set ? ( double ) instance . scaley : 1.0 , instance . scalez_set ? ( double ) instance . scalez : 1.0 ) ) ;
2018-10-22 09:45:03 +00:00
mi - > set_mirror ( Vec3d ( instance . mirrorx_set ? ( double ) instance . mirrorx : 1.0 , instance . mirrory_set ? ( double ) instance . mirrory : 1.0 , instance . mirrorz_set ? ( double ) instance . mirrorz : 1.0 ) ) ;
2019-08-06 12:54:38 +00:00
mi - > printable = instance . printable ;
2019-01-18 13:40:10 +00:00
}
2017-02-26 20:46:33 +00:00
}
}
// Load an AMF file into a provided model.
2018-09-25 09:53:05 +00:00
bool load_amf_file ( const char * path , DynamicPrintConfig * config , Model * model )
2017-02-26 20:46:33 +00:00
{
2018-02-14 13:30:03 +00:00
if ( ( path = = nullptr ) | | ( model = = nullptr ) )
return false ;
2017-02-26 20:46:33 +00:00
XML_Parser parser = XML_ParserCreate ( nullptr ) ; // encoding
2018-02-14 13:30:03 +00:00
if ( ! parser ) {
2017-02-26 20:46:33 +00:00
printf ( " Couldn't allocate memory for parser \n " ) ;
return false ;
}
2017-08-03 15:31:31 +00:00
FILE * pFile = boost : : nowide : : fopen ( path , " rt " ) ;
2017-02-26 20:46:33 +00:00
if ( pFile = = nullptr ) {
printf ( " Cannot open file %s \n " , path ) ;
return false ;
}
2018-09-25 09:53:05 +00:00
AMFParserContext ctx ( parser , config , model ) ;
2017-02-26 20:46:33 +00:00
XML_SetUserData ( parser , ( void * ) & ctx ) ;
XML_SetElementHandler ( parser , AMFParserContext : : startElement , AMFParserContext : : endElement ) ;
XML_SetCharacterDataHandler ( parser , AMFParserContext : : characters ) ;
char buff [ 8192 ] ;
bool result = false ;
for ( ; ; ) {
int len = ( int ) fread ( buff , 1 , 8192 , pFile ) ;
if ( ferror ( pFile ) ) {
printf ( " AMF parser: Read error \n " ) ;
break ;
}
int done = feof ( pFile ) ;
if ( XML_Parse ( parser , buff , len , done ) = = XML_STATUS_ERROR ) {
2019-06-25 11:06:04 +00:00
printf ( " AMF parser: Parse error at line %d: \n %s \n " ,
( int ) XML_GetCurrentLineNumber ( parser ) ,
2017-02-26 20:46:33 +00:00
XML_ErrorString ( XML_GetErrorCode ( parser ) ) ) ;
break ;
}
if ( done ) {
result = true ;
break ;
}
}
XML_ParserFree ( parser ) ;
: : fclose ( pFile ) ;
if ( result )
ctx . endDocument ( ) ;
2018-02-14 13:30:03 +00:00
2019-10-21 06:48:38 +00:00
for ( ModelObject * o : model - > objects )
{
for ( ModelVolume * v : o - > volumes )
{
if ( v - > source . input_file . empty ( ) & & ( v - > type ( ) = = ModelVolumeType : : MODEL_PART ) )
v - > source . input_file = path ;
}
}
2017-02-26 20:46:33 +00:00
return result ;
}
2019-08-23 11:12:31 +00:00
bool extract_model_from_archive ( mz_zip_archive & archive , const mz_zip_archive_file_stat & stat , DynamicPrintConfig * config , Model * model , bool check_version )
2017-02-26 20:46:33 +00:00
{
2018-02-14 13:30:03 +00:00
if ( stat . m_uncomp_size = = 0 )
{
printf ( " Found invalid size \n " ) ;
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2018-02-14 13:30:03 +00:00
return false ;
}
XML_Parser parser = XML_ParserCreate ( nullptr ) ; // encoding
if ( ! parser ) {
printf ( " Couldn't allocate memory for parser \n " ) ;
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2018-02-14 13:30:03 +00:00
return false ;
}
2018-09-25 09:53:05 +00:00
AMFParserContext ctx ( parser , config , model ) ;
2018-02-14 13:30:03 +00:00
XML_SetUserData ( parser , ( void * ) & ctx ) ;
XML_SetElementHandler ( parser , AMFParserContext : : startElement , AMFParserContext : : endElement ) ;
XML_SetCharacterDataHandler ( parser , AMFParserContext : : characters ) ;
2019-08-28 14:03:26 +00:00
struct CallbackData
2018-02-14 13:30:03 +00:00
{
2019-08-28 14:03:26 +00:00
XML_Parser & parser ;
const mz_zip_archive_file_stat & stat ;
2018-02-14 13:30:03 +00:00
2019-08-28 14:03:26 +00:00
CallbackData ( XML_Parser & parser , const mz_zip_archive_file_stat & stat ) : parser ( parser ) , stat ( stat ) { }
} ;
CallbackData data ( parser , stat ) ;
mz_bool res = 0 ;
try
{
res = mz_zip_reader_extract_file_to_callback ( & archive , stat . m_filename , [ ] ( void * pOpaque , mz_uint64 file_ofs , const void * pBuf , size_t n ) - > size_t {
CallbackData * data = ( CallbackData * ) pOpaque ;
if ( ! XML_Parse ( data - > parser , ( const char * ) pBuf , ( int ) n , ( file_ofs + n = = data - > stat . m_uncomp_size ) ? 1 : 0 ) )
{
char error_buf [ 1024 ] ;
: : sprintf ( error_buf , " Error (%s) while parsing '%s' at line %d " , XML_ErrorString ( XML_GetErrorCode ( data - > parser ) ) , data - > stat . m_filename , ( int ) XML_GetCurrentLineNumber ( data - > parser ) ) ;
throw std : : runtime_error ( error_buf ) ;
}
return n ;
} , & data , 0 ) ;
}
catch ( std : : exception & e )
2018-02-14 13:30:03 +00:00
{
2019-08-28 14:03:26 +00:00
printf ( " %s \n " , e . what ( ) ) ;
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2018-02-14 13:30:03 +00:00
return false ;
}
2019-08-28 14:03:26 +00:00
if ( res = = 0 )
2018-02-14 13:30:03 +00:00
{
2019-08-28 14:03:26 +00:00
printf ( " Error while extracting model data from zip archive " ) ;
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2018-02-14 13:30:03 +00:00
return false ;
}
ctx . endDocument ( ) ;
2019-12-19 09:22:46 +00:00
if ( check_version & & ( ctx . m_version > VERSION_AMF_COMPATIBLE ) )
2019-08-23 11:12:31 +00:00
{
2019-12-06 07:59:25 +00:00
// std::string msg = _(L("The selected amf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible."));
// throw std::runtime_error(msg.c_str());
const std : : string msg = ( boost : : format ( _ ( L ( " The selected amf file has been saved with a newer version of %1% and is not compatible. " ) ) ) % std : : string ( SLIC3R_APP_NAME ) ) . str ( ) ;
throw std : : runtime_error ( msg ) ;
2019-08-23 11:12:31 +00:00
}
2018-04-30 10:03:06 +00:00
return true ;
}
// Load an AMF archive into a provided model.
2019-08-23 11:12:31 +00:00
bool load_amf_archive ( const char * path , DynamicPrintConfig * config , Model * model , bool check_version )
2018-04-30 10:03:06 +00:00
{
if ( ( path = = nullptr ) | | ( model = = nullptr ) )
return false ;
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
2019-06-03 13:27:46 +00:00
if ( ! open_zip_reader ( & archive , path ) )
2018-04-30 10:03:06 +00:00
{
printf ( " Unable to init zip reader \n " ) ;
return false ;
}
mz_uint num_entries = mz_zip_reader_get_num_files ( & archive ) ;
mz_zip_archive_file_stat stat ;
// we first loop the entries to read from the archive the .amf file only, in order to extract the version from it
for ( mz_uint i = 0 ; i < num_entries ; + + i )
{
if ( mz_zip_reader_file_stat ( & archive , i , & stat ) )
{
if ( boost : : iends_with ( stat . m_filename , " .amf " ) )
{
2019-08-23 11:12:31 +00:00
try
{
if ( ! extract_model_from_archive ( archive , stat , config , model , check_version ) )
{
close_zip_reader ( & archive ) ;
printf ( " Archive does not contain a valid model " ) ;
return false ;
}
}
catch ( const std : : exception & e )
2018-04-30 10:03:06 +00:00
{
2019-08-23 11:12:31 +00:00
// ensure the zip archive is closed and rethrow the exception
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2019-09-02 07:38:45 +00:00
throw std : : runtime_error ( e . what ( ) ) ;
2018-04-30 10:03:06 +00:00
}
break ;
}
}
}
#if 0 // forward compatibility
// we then loop again the entries to read other files stored in the archive
for ( mz_uint i = 0 ; i < num_entries ; + + i )
{
if ( mz_zip_reader_file_stat ( & archive , i , & stat ) )
{
// add code to extract the file
}
}
# endif // forward compatibility
2019-06-03 13:27:46 +00:00
close_zip_reader ( & archive ) ;
2018-02-14 13:30:03 +00:00
return true ;
}
// Load an AMF file into a provided model.
2018-09-25 09:53:05 +00:00
// If config is not a null pointer, updates it if the amf file/archive contains config data
2019-08-23 11:12:31 +00:00
bool load_amf ( const char * path , DynamicPrintConfig * config , Model * model , bool check_version )
2018-02-14 13:30:03 +00:00
{
2018-05-24 07:57:12 +00:00
if ( boost : : iends_with ( path , " .amf.xml " ) )
// backward compatibility with older slic3r output
2018-09-25 09:53:05 +00:00
return load_amf_file ( path , config , model ) ;
2018-05-24 07:57:12 +00:00
else if ( boost : : iends_with ( path , " .amf " ) )
{
boost : : nowide : : ifstream file ( path , boost : : nowide : : ifstream : : binary ) ;
if ( ! file . good ( ) )
return false ;
std : : string zip_mask ( 2 , ' \0 ' ) ;
2020-01-03 15:32:56 +00:00
file . read ( zip_mask . data ( ) , 2 ) ;
2018-05-24 07:57:12 +00:00
file . close ( ) ;
2019-08-23 11:12:31 +00:00
return ( zip_mask = = " PK " ) ? load_amf_archive ( path , config , model , check_version ) : load_amf_file ( path , config , model ) ;
2018-05-24 07:57:12 +00:00
}
2018-02-14 13:30:03 +00:00
else
return false ;
}
2020-01-08 10:11:38 +00:00
bool store_amf ( const char * path , Model * model , const DynamicPrintConfig * config , bool fullpath_sources )
2018-02-14 13:30:03 +00:00
{
2018-11-07 13:57:50 +00:00
if ( ( path = = nullptr ) | | ( model = = nullptr ) )
2018-02-14 13:30:03 +00:00
return false ;
2018-03-26 06:58:44 +00:00
// forces ".zip.amf" extension
std : : string export_path = path ;
if ( ! boost : : iends_with ( export_path , " .zip.amf " ) )
export_path = boost : : filesystem : : path ( export_path ) . replace_extension ( " .zip.amf " ) . string ( ) ;
2018-02-14 13:30:03 +00:00
mz_zip_archive archive ;
mz_zip_zero_struct ( & archive ) ;
2019-06-03 13:27:46 +00:00
if ( ! open_zip_writer ( & archive , export_path ) ) return false ;
2018-02-14 13:30:03 +00:00
std : : stringstream stream ;
2019-02-27 09:31:53 +00:00
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
// 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).
// It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
// The default value of std::stream precision is 6 digits only!
2019-02-27 10:08:58 +00:00
stream < < std : : setprecision ( std : : numeric_limits < float > : : max_digits10 ) ;
2018-02-14 13:30:03 +00:00
stream < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n " ;
stream < < " <amf unit= \" millimeter \" > \n " ;
stream < < " <metadata type= \" cad \" >Slic3r " < < SLIC3R_VERSION < < " </metadata> \n " ;
2018-04-30 10:03:06 +00:00
stream < < " <metadata type= \" " < < SLIC3RPE_AMF_VERSION < < " \" > " < < VERSION_AMF < < " </metadata> \n " ;
2018-02-14 13:30:03 +00:00
2018-11-07 13:57:50 +00:00
if ( config ! = nullptr )
2018-03-22 12:49:48 +00:00
{
2018-11-07 13:57:50 +00:00
std : : string str_config = " \n " ;
for ( const std : : string & key : config - > keys ( ) )
if ( key ! = " compatible_printers " )
2019-06-26 11:26:49 +00:00
str_config + = " ; " + key + " = " + config - > opt_serialize ( key ) + " \n " ;
2018-11-07 13:57:50 +00:00
stream < < " <metadata type= \" " < < SLIC3R_CONFIG_TYPE < < " \" > " < < xml_escape ( str_config ) < < " </metadata> \n " ;
2018-03-22 12:49:48 +00:00
}
2017-02-26 20:46:33 +00:00
for ( const auto & material : model - > materials ) {
if ( material . first . empty ( ) )
continue ;
// note that material-id must never be 0 since it's reserved by the AMF spec
2018-02-14 13:30:03 +00:00
stream < < " <material id= \" " < < material . first < < " \" > \n " ;
2017-02-26 20:46:33 +00:00
for ( const auto & attr : material . second - > attributes )
2018-02-14 13:30:03 +00:00
stream < < " <metadata type= \" " < < attr . first < < " \" > " < < attr . second < < " </metadata> \n " ;
2017-02-26 20:46:33 +00:00
for ( const std : : string & key : material . second - > config . keys ( ) )
2019-06-26 11:26:49 +00:00
stream < < " <metadata type= \" slic3r. " < < key < < " \" > " < < material . second - > config . opt_serialize ( key ) < < " </metadata> \n " ;
2018-02-14 13:30:03 +00:00
stream < < " </material> \n " ;
2017-02-26 20:46:33 +00:00
}
std : : string instances ;
for ( size_t object_id = 0 ; object_id < model - > objects . size ( ) ; + + object_id ) {
ModelObject * object = model - > objects [ object_id ] ;
2018-02-14 13:30:03 +00:00
stream < < " <object id= \" " < < object_id < < " \" > \n " ;
2017-02-26 20:46:33 +00:00
for ( const std : : string & key : object - > config . keys ( ) )
2019-06-26 11:26:49 +00:00
stream < < " <metadata type= \" slic3r. " < < key < < " \" > " < < object - > config . opt_serialize ( key ) < < " </metadata> \n " ;
2018-02-14 13:30:03 +00:00
if ( ! object - > name . empty ( ) )
2018-07-23 08:58:39 +00:00
stream < < " <metadata type= \" name \" > " < < xml_escape ( object - > name ) < < " </metadata> \n " ;
2019-01-21 09:06:51 +00:00
const std : : vector < double > & layer_height_profile = object - > layer_height_profile ;
2017-02-26 20:46:33 +00:00
if ( layer_height_profile . size ( ) > = 4 & & ( layer_height_profile . size ( ) % 2 ) = = 0 ) {
// Store the layer height profile as a single semicolon separated list.
2018-02-14 13:30:03 +00:00
stream < < " <metadata type= \" slic3r.layer_height_profile \" > " ;
stream < < layer_height_profile . front ( ) ;
for ( size_t i = 1 ; i < layer_height_profile . size ( ) ; + + i )
stream < < " ; " < < layer_height_profile [ i ] ;
2019-09-24 14:01:01 +00:00
stream < < " \n </metadata> \n " ;
2017-02-26 20:46:33 +00:00
}
2018-10-03 13:13:54 +00:00
2019-07-04 18:49:46 +00:00
// Export layer height ranges including the layer range specific config overrides.
2019-06-11 07:50:58 +00:00
const t_layer_config_ranges & config_ranges = object - > layer_config_ranges ;
if ( ! config_ranges . empty ( ) )
{
// Store the layer config range as a single semicolon separated list.
stream < < " <layer_config_ranges> \n " ;
size_t layer_counter = 0 ;
for ( auto range : config_ranges ) {
stream < < " <range id= \" " < < layer_counter < < " \" > \n " ;
2019-06-20 14:15:09 +00:00
stream < < " <metadata type= \" slic3r.layer_height_range \" > " ;
2019-06-11 07:50:58 +00:00
stream < < range . first . first < < " ; " < < range . first . second < < " </metadata> \n " ;
for ( const std : : string & key : range . second . keys ( ) )
2019-07-04 18:49:46 +00:00
stream < < " <metadata type= \" slic3r. " < < key < < " \" > " < < range . second . opt_serialize ( key ) < < " </metadata> \n " ;
2019-06-11 07:50:58 +00:00
stream < < " </range> \n " ;
layer_counter + + ;
}
stream < < " </layer_config_ranges> \n " ;
}
2018-10-03 13:13:54 +00:00
2019-01-30 07:26:23 +00:00
const std : : vector < sla : : SupportPoint > & sla_support_points = object - > sla_support_points ;
2018-10-03 13:13:54 +00:00
if ( ! sla_support_points . empty ( ) ) {
// Store the SLA supports as a single semicolon separated list.
stream < < " <metadata type= \" slic3r.sla_support_points \" > " ;
for ( size_t i = 0 ; i < sla_support_points . size ( ) ; + + i ) {
if ( i ! = 0 )
stream < < " ; " ;
2019-01-30 07:26:23 +00:00
stream < < 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 < < " ; " < < sla_support_points [ i ] . is_new_island ;
2018-10-03 13:13:54 +00:00
}
stream < < " \n </metadata> \n " ;
}
2018-02-14 13:30:03 +00:00
stream < < " <mesh> \n " ;
stream < < " <vertices> \n " ;
2017-02-26 20:46:33 +00:00
std : : vector < int > vertices_offsets ;
int num_vertices = 0 ;
for ( ModelVolume * volume : object - > volumes ) {
vertices_offsets . push_back ( num_vertices ) ;
2019-06-11 15:08:47 +00:00
if ( ! volume - > mesh ( ) . repaired )
2018-09-18 08:09:58 +00:00
throw std : : runtime_error ( " store_amf() requires repair() " ) ;
2019-06-11 15:08:47 +00:00
if ( ! volume - > mesh ( ) . has_shared_vertices ( ) )
throw std : : runtime_error ( " store_amf() requires shared vertices " ) ;
const indexed_triangle_set & its = volume - > mesh ( ) . its ;
2019-01-24 12:32:45 +00:00
const Transform3d & matrix = volume - > get_matrix ( ) ;
2019-06-10 16:30:54 +00:00
for ( size_t i = 0 ; i < its . vertices . size ( ) ; + + i ) {
2018-02-14 13:30:03 +00:00
stream < < " <vertex> \n " ;
stream < < " <coordinates> \n " ;
2019-06-10 16:30:54 +00:00
Vec3f v = ( matrix * its . vertices [ i ] . cast < double > ( ) ) . cast < float > ( ) ;
2019-01-24 12:32:45 +00:00
stream < < " <x> " < < v ( 0 ) < < " </x> \n " ;
stream < < " <y> " < < v ( 1 ) < < " </y> \n " ;
stream < < " <z> " < < v ( 2 ) < < " </z> \n " ;
2018-02-14 13:30:03 +00:00
stream < < " </coordinates> \n " ;
stream < < " </vertex> \n " ;
2017-02-26 20:46:33 +00:00
}
2019-06-20 14:15:09 +00:00
num_vertices + = ( int ) its . vertices . size ( ) ;
2017-02-26 20:46:33 +00:00
}
2018-02-14 13:30:03 +00:00
stream < < " </vertices> \n " ;
for ( size_t i_volume = 0 ; i_volume < object - > volumes . size ( ) ; + + i_volume ) {
2017-02-26 20:46:33 +00:00
ModelVolume * volume = object - > volumes [ i_volume ] ;
int vertices_offset = vertices_offsets [ i_volume ] ;
if ( volume - > material_id ( ) . empty ( ) )
2018-02-14 13:30:03 +00:00
stream < < " <volume> \n " ;
2017-02-26 20:46:33 +00:00
else
2018-02-14 13:30:03 +00:00
stream < < " <volume materialid= \" " < < volume - > material_id ( ) < < " \" > \n " ;
2017-02-26 20:46:33 +00:00
for ( const std : : string & key : volume - > config . keys ( ) )
2019-06-26 11:26:49 +00:00
stream < < " <metadata type= \" slic3r. " < < key < < " \" > " < < volume - > config . opt_serialize ( key ) < < " </metadata> \n " ;
2018-02-14 13:30:03 +00:00
if ( ! volume - > name . empty ( ) )
2018-07-23 08:58:39 +00:00
stream < < " <metadata type= \" name \" > " < < xml_escape ( volume - > name ) < < " </metadata> \n " ;
2018-09-17 13:12:13 +00:00
if ( volume - > is_modifier ( ) )
2018-02-14 13:30:03 +00:00
stream < < " <metadata type= \" slic3r.modifier \" >1</metadata> \n " ;
2018-09-17 13:12:13 +00:00
stream < < " <metadata type= \" slic3r.volume_type \" > " < < ModelVolume : : type_to_string ( volume - > type ( ) ) < < " </metadata> \n " ;
2019-09-19 10:39:59 +00:00
stream < < " <metadata type= \" slic3r.matrix \" > " ;
2019-12-20 11:11:58 +00:00
const Transform3d & matrix = volume - > get_matrix ( ) * volume - > source . transform . get_matrix ( ) ;
stream < < std : : setprecision ( std : : numeric_limits < double > : : max_digits10 ) ;
2019-09-19 10:39:59 +00:00
for ( int r = 0 ; r < 4 ; + + r )
{
for ( int c = 0 ; c < 4 ; + + c )
{
stream < < matrix ( r , c ) ;
if ( ( r ! = 3 ) | | ( c ! = 3 ) )
stream < < " " ;
}
}
stream < < " </metadata> \n " ;
if ( ! volume - > source . input_file . empty ( ) )
{
2020-01-08 10:11:38 +00:00
std : : string input_file = xml_escape ( fullpath_sources ? volume - > source . input_file : boost : : filesystem : : path ( volume - > source . input_file ) . filename ( ) . string ( ) ) ;
stream < < " <metadata type= \" slic3r.source_file \" > " < < input_file < < " </metadata> \n " ;
2019-09-19 10:39:59 +00:00
stream < < " <metadata type= \" slic3r.source_object_id \" > " < < volume - > source . object_idx < < " </metadata> \n " ;
stream < < " <metadata type= \" slic3r.source_volume_id \" > " < < volume - > source . volume_idx < < " </metadata> \n " ;
stream < < " <metadata type= \" slic3r.source_offset_x \" > " < < volume - > source . mesh_offset ( 0 ) < < " </metadata> \n " ;
stream < < " <metadata type= \" slic3r.source_offset_y \" > " < < volume - > source . mesh_offset ( 1 ) < < " </metadata> \n " ;
stream < < " <metadata type= \" slic3r.source_offset_z \" > " < < volume - > source . mesh_offset ( 2 ) < < " </metadata> \n " ;
}
2019-12-19 09:22:46 +00:00
stream < < std : : setprecision ( std : : numeric_limits < float > : : max_digits10 ) ;
2019-09-19 10:39:59 +00:00
const indexed_triangle_set & its = volume - > mesh ( ) . its ;
2019-06-25 11:06:04 +00:00
for ( size_t i = 0 ; i < its . indices . size ( ) ; + + i ) {
2018-02-14 13:30:03 +00:00
stream < < " <triangle> \n " ;
for ( int j = 0 ; j < 3 ; + + j )
2019-06-11 15:08:47 +00:00
stream < < " <v " < < j + 1 < < " > " < < its . indices [ i ] [ j ] + vertices_offset < < " </v " < < j + 1 < < " > \n " ;
2018-02-14 13:30:03 +00:00
stream < < " </triangle> \n " ;
2017-02-26 20:46:33 +00:00
}
2018-02-14 13:30:03 +00:00
stream < < " </volume> \n " ;
2017-02-26 20:46:33 +00:00
}
2018-02-14 13:30:03 +00:00
stream < < " </mesh> \n " ;
stream < < " </object> \n " ;
if ( ! object - > instances . empty ( ) ) {
2017-02-26 20:46:33 +00:00
for ( ModelInstance * instance : object - > instances ) {
char buf [ 512 ] ;
sprintf ( buf ,
2017-02-26 22:13:31 +00:00
" <instance objectid= \" " PRINTF_ZU " \" > \n "
" <deltax>%lf</deltax> \n "
" <deltay>%lf</deltay> \n "
2018-09-17 13:12:13 +00:00
" <deltaz>%lf</deltaz> \n "
2018-09-20 13:00:40 +00:00
" <rx>%lf</rx> \n "
" <ry>%lf</ry> \n "
2017-02-26 22:13:31 +00:00
" <rz>%lf</rz> \n "
2018-09-24 13:54:09 +00:00
" <scalex>%lf</scalex> \n "
" <scaley>%lf</scaley> \n "
" <scalez>%lf</scalez> \n "
2018-10-22 09:45:03 +00:00
" <mirrorx>%lf</mirrorx> \n "
" <mirrory>%lf</mirrory> \n "
" <mirrorz>%lf</mirrorz> \n "
2019-08-06 12:54:38 +00:00
" <printable>%d</printable> \n "
2017-02-26 20:46:33 +00:00
" </instance> \n " ,
object_id ,
2018-09-17 13:12:13 +00:00
instance - > get_offset ( X ) ,
instance - > get_offset ( Y ) ,
instance - > get_offset ( Z ) ,
2018-09-20 13:00:40 +00:00
instance - > get_rotation ( X ) ,
instance - > get_rotation ( Y ) ,
instance - > get_rotation ( Z ) ,
2018-09-24 13:54:09 +00:00
instance - > get_scaling_factor ( X ) ,
instance - > get_scaling_factor ( Y ) ,
2018-10-22 09:45:03 +00:00
instance - > get_scaling_factor ( Z ) ,
instance - > get_mirror ( X ) ,
instance - > get_mirror ( Y ) ,
2019-08-06 12:54:38 +00:00
instance - > get_mirror ( Z ) ,
instance - > printable ) ;
2018-09-20 13:00:40 +00:00
2017-02-26 20:46:33 +00:00
//FIXME missing instance->scaling_factor
instances . append ( buf ) ;
}
}
}
if ( ! instances . empty ( ) ) {
2018-02-14 13:30:03 +00:00
stream < < " <constellation id= \" 1 \" > \n " ;
stream < < instances ;
stream < < " </constellation> \n " ;
2017-02-26 20:46:33 +00:00
}
2019-11-11 15:01:34 +00:00
2020-01-16 15:01:19 +00:00
if ( ! model - > custom_gcode_per_print_z . gcodes . empty ( ) )
2019-11-11 15:01:34 +00:00
{
std : : string out = " " ;
pt : : ptree tree ;
pt : : ptree & main_tree = tree . add ( " custom_gcodes_per_height " , " " ) ;
2020-01-23 11:49:39 +00:00
for ( const CustomGCode : : Item & code : model - > custom_gcode_per_print_z . gcodes )
2019-11-11 15:01:34 +00:00
{
pt : : ptree & code_tree = main_tree . add ( " code " , " " ) ;
2020-01-23 11:49:39 +00:00
// store custom_gcode_per_print_z gcodes information
2019-12-17 07:37:50 +00:00
code_tree . put ( " <xmlattr>.print_z " , code . print_z ) ;
code_tree . put ( " <xmlattr>.gcode " , code . gcode ) ;
code_tree . put ( " <xmlattr>.extruder " , code . extruder ) ;
code_tree . put ( " <xmlattr>.color " , code . color ) ;
2019-11-11 15:01:34 +00:00
}
2020-01-23 11:49:39 +00:00
pt : : ptree & mode_tree = main_tree . add ( " mode " , " " ) ;
// store mode of a custom_gcode_per_print_z
mode_tree . put ( " <xmlattr>.value " ,
model - > custom_gcode_per_print_z . mode = = CustomGCode : : Mode : : SingleExtruder ? CustomGCode : : SingleExtruderMode :
model - > custom_gcode_per_print_z . mode = = CustomGCode : : Mode : : MultiAsSingle ?
CustomGCode : : MultiAsSingleMode : CustomGCode : : MultiExtruderMode ) ;
2019-11-11 15:01:34 +00:00
if ( ! tree . empty ( ) )
{
std : : ostringstream oss ;
pt : : write_xml ( oss , tree ) ;
out = oss . str ( ) ;
2019-12-20 11:11:58 +00:00
size_t del_header_pos = out . find ( " <custom_gcodes_per_height " ) ;
2019-11-11 15:01:34 +00:00
if ( del_header_pos ! = std : : string : : npos )
out . erase ( out . begin ( ) , out . begin ( ) + del_header_pos ) ;
// Post processing("beautification") of the output string
boost : : replace_all ( out , " ><code " , " > \n <code " ) ;
2020-01-23 11:49:39 +00:00
boost : : replace_all ( out , " ><mode " , " > \n <mode " ) ;
2019-11-11 15:01:34 +00:00
boost : : replace_all ( out , " >< " , " > \n < " ) ;
stream < < out < < " \n " ;
}
}
2018-02-14 13:30:03 +00:00
stream < < " </amf> \n " ;
2018-03-26 06:58:44 +00:00
std : : string internal_amf_filename = boost : : ireplace_last_copy ( boost : : filesystem : : path ( export_path ) . filename ( ) . string ( ) , " .zip.amf " , " .amf " ) ;
2018-02-14 13:30:03 +00:00
std : : string out = stream . str ( ) ;
2018-02-14 14:52:39 +00:00
if ( ! mz_zip_writer_add_mem ( & archive , internal_amf_filename . c_str ( ) , ( const void * ) out . data ( ) , out . length ( ) , MZ_DEFAULT_COMPRESSION ) )
2018-02-14 13:30:03 +00:00
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-03-26 06:58:44 +00:00
boost : : filesystem : : remove ( export_path ) ;
2018-02-14 13:30:03 +00:00
return false ;
}
if ( ! mz_zip_writer_finalize_archive ( & archive ) )
{
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-03-26 06:58:44 +00:00
boost : : filesystem : : remove ( export_path ) ;
2018-02-14 13:30:03 +00:00
return false ;
}
2019-06-03 13:27:46 +00:00
close_zip_writer ( & archive ) ;
2018-02-14 13:30:03 +00:00
2017-02-26 20:46:33 +00:00
return true ;
}
} ; // namespace Slic3r