use crate::document::CadDocument;
use crate::entities::*;
use crate::error::Result;
use crate::objects::{
Dictionary, DictionaryVariable, DictionaryWithDefault, Group, ImageDefinition,
ImageDefinitionReactor, Layout, MLineStyle, Material, MultiLeaderStyle,
ObjectType, PlotSettings, RasterVariables, Scale, SortEntitiesTable,
TableStyle, VisualStyle, BookColor, WipeoutVariables, XRecord,
};
use crate::tables::*;
use crate::types::{Color, DxfVersion, Handle, Vector3};
use crate::xdata::{ExtendedData, XDataValue};
use std::collections::HashSet;
use super::stream_writer::{DxfStreamWriter, DxfStreamWriterExt};
pub struct SectionWriter<'a, W: DxfStreamWriter> {
writer: &'a mut W,
next_handle: u64,
handle_seed: u64,
dxf_version: DxfVersion,
sab_entries: Vec<(Handle, Vec<u8>)>,
writing_paper_space: bool,
valid_handles: HashSet<Handle>,
bylayer_linetype_handle: Handle,
byblock_linetype_handle: Handle,
}
impl<'a, W: DxfStreamWriter> SectionWriter<'a, W> {
pub fn new(writer: &'a mut W, handle_start: u64, handle_seed: u64) -> Self {
Self {
writer,
next_handle: handle_start,
handle_seed,
dxf_version: DxfVersion::AC1024,
sab_entries: Vec::new(),
writing_paper_space: false,
valid_handles: HashSet::new(),
bylayer_linetype_handle: Handle::NULL,
byblock_linetype_handle: Handle::NULL,
}
}
pub fn build_valid_handles(&mut self, document: &CadDocument) {
let mut set = HashSet::new();
for h in document.objects.keys() {
set.insert(*h);
}
for br in document.block_records.iter() {
set.insert(br.handle());
for eh in &br.entity_handles {
set.insert(*eh);
}
}
for h in document.entity_index.keys() {
set.insert(*h);
}
for r in document.layers.iter() { set.insert(r.handle()); }
for r in document.line_types.iter() { set.insert(r.handle()); }
for r in document.text_styles.iter() { set.insert(r.handle()); }
for r in document.dim_styles.iter() { set.insert(r.handle()); }
for r in document.app_ids.iter() { set.insert(r.handle()); }
for r in document.views.iter() { set.insert(r.handle()); }
for r in document.vports.iter() { set.insert(r.handle()); }
for r in document.ucss.iter() { set.insert(r.handle()); }
self.valid_handles = set;
if let Some(lt) = document.line_types.get("ByLayer") {
self.bylayer_linetype_handle = lt.handle();
}
if let Some(lt) = document.line_types.get("ByBlock") {
self.byblock_linetype_handle = lt.handle();
}
}
pub fn set_version(&mut self, version: DxfVersion) {
self.dxf_version = version;
}
fn needs_sab(&self) -> bool {
self.dxf_version >= DxfVersion::AC1027
}
fn allocate_handle(&mut self) -> Handle {
let handle = Handle::new(self.next_handle);
self.next_handle += 1;
handle
}
pub fn write_header(&mut self, document: &CadDocument) -> Result<()> {
self.writer.write_section_start("HEADER")?;
let hdr = &document.header;
self.write_header_variable("$ACADVER", |w| {
w.write_string(1, document.version.to_dxf_string())
})?;
self.write_header_variable("$ACADMAINTVER", |w| w.write_i16(70, 0))?;
self.write_header_variable("$DWGCODEPAGE", |w| w.write_string(3, &hdr.code_page))?;
let handle_seed = self.handle_seed;
self.write_header_variable("$HANDSEED", |w| w.write_handle(5, Handle::new(handle_seed)))?;
self.write_header_variable("$INSBASE", |w| {
let v = &hdr.model_space_insertion_base;
w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
})?;
self.write_header_variable("$EXTMIN", |w| {
let v = &hdr.model_space_extents_min;
w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
})?;
self.write_header_variable("$EXTMAX", |w| {
let v = &hdr.model_space_extents_max;
w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
})?;
self.write_header_variable("$LIMMIN", |w| {
let v = &hdr.model_space_limits_min;
w.write_double(10, v.x)?; w.write_double(20, v.y)
})?;
self.write_header_variable("$LIMMAX", |w| {
let v = &hdr.model_space_limits_max;
w.write_double(10, v.x)?; w.write_double(20, v.y)
})?;
self.write_header_variable("$ORTHOMODE", |w| w.write_i16(70, if hdr.ortho_mode { 1 } else { 0 }))?;
self.write_header_variable("$REGENMODE", |w| w.write_i16(70, if hdr.regen_mode { 1 } else { 0 }))?;
self.write_header_variable("$FILLMODE", |w| w.write_i16(70, if hdr.fill_mode { 1 } else { 0 }))?;
self.write_header_variable("$QTEXTMODE", |w| w.write_i16(70, if hdr.quick_text_mode { 1 } else { 0 }))?;
self.write_header_variable("$MIRRTEXT", |w| w.write_i16(70, if hdr.mirror_text { 1 } else { 0 }))?;
self.write_header_variable("$LTSCALE", |w| w.write_double(40, hdr.linetype_scale))?;
self.write_header_variable("$ATTMODE", |w| w.write_i16(70, hdr.attribute_visibility))?;
self.write_header_variable("$TEXTSIZE", |w| w.write_double(40, hdr.text_height))?;
self.write_header_variable("$TRACEWID", |w| w.write_double(40, hdr.trace_width))?;
self.write_header_variable("$TEXTSTYLE", |w| w.write_string(7, &hdr.current_text_style_name))?;
self.write_header_variable("$CMLSTYLE", |w| w.write_string(2, &hdr.multiline_style))?;
self.write_header_variable("$CLAYER", |w| w.write_string(8, &hdr.current_layer_name))?;
self.write_header_variable("$CELTYPE", |w| w.write_string(6, &hdr.current_linetype_name))?;
self.write_header_variable("$CECOLOR", |w| w.write_i16(62, hdr.current_entity_color.approximate_index()))?;
self.write_header_variable("$CELWEIGHT", |w| w.write_i16(370, hdr.current_line_weight))?;
self.write_header_variable("$CELTSCALE", |w| w.write_double(40, hdr.current_entity_linetype_scale))?;
self.write_header_variable("$DISPSILH", |w| w.write_i16(70, if hdr.display_silhouette { 1 } else { 0 }))?;
self.write_header_variable("$LUNITS", |w| w.write_i16(70, hdr.linear_unit_format))?;
self.write_header_variable("$LUPREC", |w| w.write_i16(70, hdr.linear_unit_precision))?;
self.write_header_variable("$AUNITS", |w| w.write_i16(70, hdr.angular_unit_format))?;
self.write_header_variable("$AUPREC", |w| w.write_i16(70, hdr.angular_unit_precision))?;
self.write_header_variable("$MEASUREMENT", |w| w.write_i16(70, hdr.measurement))?;
self.write_header_variable("$INSUNITS", |w| w.write_i16(70, hdr.insertion_units))?;
self.write_header_variable("$PDMODE", |w| w.write_i16(70, hdr.point_display_mode))?;
self.write_header_variable("$PDSIZE", |w| w.write_double(40, hdr.point_display_size))?;
self.write_header_variable("$PLINEGEN", |w| w.write_i16(70, if hdr.polyline_linetype_generation { 1 } else { 0 }))?;
self.write_header_variable("$PSLTSCALE", |w| w.write_i16(70, if hdr.paper_space_linetype_scaling { 1 } else { 0 }))?;
self.write_header_variable("$DIMSCALE", |w| w.write_double(40, hdr.dim_scale))?;
self.write_header_variable("$DIMASZ", |w| w.write_double(40, hdr.dim_arrow_size))?;
self.write_header_variable("$DIMEXO", |w| w.write_double(40, hdr.dim_ext_line_offset))?;
self.write_header_variable("$DIMDLI", |w| w.write_double(40, hdr.dim_line_increment))?;
self.write_header_variable("$DIMRND", |w| w.write_double(40, hdr.dim_rounding))?;
self.write_header_variable("$DIMDLE", |w| w.write_double(40, hdr.dim_line_extension))?;
self.write_header_variable("$DIMEXE", |w| w.write_double(40, hdr.dim_ext_line_extension))?;
self.write_header_variable("$DIMTP", |w| w.write_double(40, hdr.dim_tolerance_plus))?;
self.write_header_variable("$DIMTM", |w| w.write_double(40, hdr.dim_tolerance_minus))?;
self.write_header_variable("$DIMTXT", |w| w.write_double(40, hdr.dim_text_height))?;
self.write_header_variable("$DIMCEN", |w| w.write_double(40, hdr.dim_center_mark))?;
self.write_header_variable("$DIMTSZ", |w| w.write_double(40, hdr.dim_tick_size))?;
self.write_header_variable("$DIMTOL", |w| w.write_i16(70, if hdr.dim_tolerance { 1 } else { 0 }))?;
self.write_header_variable("$DIMLIM", |w| w.write_i16(70, if hdr.dim_limits { 1 } else { 0 }))?;
self.write_header_variable("$DIMTIH", |w| w.write_i16(70, if hdr.dim_text_inside_horizontal { 1 } else { 0 }))?;
self.write_header_variable("$DIMTOH", |w| w.write_i16(70, if hdr.dim_text_outside_horizontal { 1 } else { 0 }))?;
self.write_header_variable("$DIMSE1", |w| w.write_i16(70, if hdr.dim_suppress_ext1 { 1 } else { 0 }))?;
self.write_header_variable("$DIMSE2", |w| w.write_i16(70, if hdr.dim_suppress_ext2 { 1 } else { 0 }))?;
self.write_header_variable("$DIMTAD", |w| w.write_i16(70, hdr.dim_text_above))?;
self.write_header_variable("$DIMZIN", |w| w.write_i16(70, hdr.dim_zero_suppression))?;
self.write_header_variable("$DIMCLRD", |w| w.write_i16(70, hdr.dim_line_color.approximate_index()))?;
self.write_header_variable("$DIMCLRE", |w| w.write_i16(70, hdr.dim_ext_line_color.approximate_index()))?;
self.write_header_variable("$DIMCLRT", |w| w.write_i16(70, hdr.dim_text_color.approximate_index()))?;
self.write_header_variable("$DIMGAP", |w| w.write_double(40, hdr.dim_line_gap))?;
self.write_header_variable("$DIMALT", |w| w.write_i16(70, if hdr.dim_alternate_units { 1 } else { 0 }))?;
self.write_header_variable("$DIMALTD", |w| w.write_i16(70, hdr.dim_alt_decimal_places))?;
self.write_header_variable("$DIMALTF", |w| w.write_double(40, hdr.dim_alt_scale))?;
self.write_header_variable("$DIMLFAC", |w| w.write_double(40, hdr.dim_linear_scale))?;
self.write_header_variable("$DIMTOFL", |w| w.write_i16(70, if hdr.dim_force_line_inside { 1 } else { 0 }))?;
self.write_header_variable("$DIMTVP", |w| w.write_double(40, hdr.dim_text_vertical_pos))?;
self.write_header_variable("$DIMTIX", |w| w.write_i16(70, if hdr.dim_force_text_inside { 1 } else { 0 }))?;
self.write_header_variable("$DIMSOXD", |w| w.write_i16(70, if hdr.dim_suppress_outside_ext { 1 } else { 0 }))?;
self.write_header_variable("$DIMSAH", |w| w.write_i16(70, if hdr.dim_separate_arrows { 1 } else { 0 }))?;
self.write_header_variable("$DIMPOST", |w| w.write_string(1, &hdr.dim_post))?;
self.write_header_variable("$DIMAPOST", |w| w.write_string(1, &hdr.dim_alt_post))?;
self.write_header_variable("$DIMSTYLE", |w| w.write_string(2, &hdr.current_dimstyle_name))?;
self.write_header_variable("$DIMLUNIT", |w| w.write_i16(70, hdr.dim_linear_unit_format))?;
self.write_header_variable("$DIMDEC", |w| w.write_i16(70, hdr.dim_decimal_places))?;
self.write_header_variable("$DIMTDEC", |w| w.write_i16(70, hdr.dim_tolerance_decimal_places))?;
self.write_header_variable("$DIMALTU", |w| w.write_i16(70, hdr.dim_alt_units_format))?;
self.write_header_variable("$DIMALTTD", |w| w.write_i16(70, hdr.dim_alt_tolerance_decimal_places))?;
self.write_header_variable("$DIMAUNIT", |w| w.write_i16(70, hdr.dim_angular_units))?;
self.write_header_variable("$DIMADEC", |w| w.write_i16(70, hdr.dim_angular_decimal_places))?;
self.write_header_variable("$DIMJUST", |w| w.write_i16(70, hdr.dim_horizontal_justification))?;
self.write_header_variable("$DIMSD1", |w| w.write_i16(70, if hdr.dim_suppress_line1 { 1 } else { 0 }))?;
self.write_header_variable("$DIMSD2", |w| w.write_i16(70, if hdr.dim_suppress_line2 { 1 } else { 0 }))?;
self.write_header_variable("$DIMTOLJ", |w| w.write_i16(70, hdr.dim_tolerance_justification))?;
self.write_header_variable("$DIMTZIN", |w| w.write_i16(70, hdr.dim_tolerance_zero_suppression))?;
self.write_header_variable("$DIMALTZ", |w| w.write_i16(70, hdr.dim_alt_tolerance_zero_suppression))?;
self.write_header_variable("$DIMALTTZ", |w| w.write_i16(70, hdr.dim_alt_tolerance_zero_tight))?;
self.write_header_variable("$DIMATFIT", |w| w.write_i16(70, hdr.dim_fit))?;
self.write_header_variable("$DIMDSEP", |w| w.write_i16(70, hdr.dim_decimal_separator as i16))?;
self.write_header_variable("$DIMTMOVE", |w| w.write_i16(70, hdr.dim_text_movement))?;
self.write_header_variable("$DIMFRAC", |w| w.write_i16(70, hdr.dim_fraction_format))?;
self.write_header_variable("$DIMLWD", |w| w.write_i16(70, hdr.dim_line_weight))?;
self.write_header_variable("$DIMLWE", |w| w.write_i16(70, hdr.dim_ext_line_weight))?;
self.write_header_variable("$DIMTFAC", |w| w.write_double(40, hdr.dim_tolerance_scale))?;
self.write_header_variable("$SPLFRAME", |w| w.write_i16(70, if hdr.spline_frame { 1 } else { 0 }))?;
self.write_header_variable("$SPLINETYPE", |w| w.write_i16(70, hdr.spline_type))?;
self.write_header_variable("$SPLINESEGS", |w| w.write_i16(70, hdr.spline_segments))?;
self.write_header_variable("$SURFTAB1", |w| w.write_i16(70, hdr.surface_tab1))?;
self.write_header_variable("$SURFTAB2", |w| w.write_i16(70, hdr.surface_tab2))?;
self.write_header_variable("$SURFTYPE", |w| w.write_i16(70, hdr.surface_type))?;
self.write_header_variable("$SURFU", |w| w.write_i16(70, hdr.surface_u_density))?;
self.write_header_variable("$SURFV", |w| w.write_i16(70, hdr.surface_v_density))?;
self.write_header_variable("$WORLDVIEW", |w| w.write_i16(70, if hdr.world_view { 1 } else { 0 }))?;
self.write_header_variable("$PELEVATION", |w| w.write_double(40, hdr.paper_elevation))?;
self.write_header_variable("$PLINEWID", |w| w.write_double(40, hdr.polyline_width))?;
self.write_header_variable("$MAXACTVP", |w| w.write_i16(70, hdr.max_active_viewports))?;
self.write_header_variable("$TILEMODE", |w| w.write_i16(70, if hdr.show_model_space { 1 } else { 0 }))?;
self.write_header_variable("$PLIMCHECK", |w| w.write_i16(70, if hdr.paper_space_limit_check { 1 } else { 0 }))?;
self.write_header_variable("$VISRETAIN", |w| w.write_i16(70, if hdr.retain_xref_visibility { 1 } else { 0 }))?;
self.write_header_variable("$TDCREATE", |w| w.write_double(40, hdr.create_date_julian))?;
self.write_header_variable("$TDUPDATE", |w| w.write_double(40, hdr.update_date_julian))?;
self.write_header_variable("$TDINDWG", |w| w.write_double(40, hdr.total_editing_time))?;
self.write_header_variable("$UCSORG", |w| {
let v = &hdr.model_space_ucs_origin;
w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
})?;
self.write_header_variable("$UCSXDIR", |w| {
let v = &hdr.model_space_ucs_x_axis;
w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
})?;
self.write_header_variable("$UCSYDIR", |w| {
let v = &hdr.model_space_ucs_y_axis;
w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
})?;
self.writer.write_section_end()?;
Ok(())
}
fn write_header_variable<F>(&mut self, name: &str, write_value: F) -> Result<()>
where
F: FnOnce(&mut W) -> Result<()>,
{
self.writer.write_string(9, name)?;
write_value(self.writer)
}
pub fn write_classes(&mut self, document: &CadDocument) -> Result<()> {
self.writer.write_section_start("CLASSES")?;
for class in document.classes.iter() {
self.writer.write_string(0, "CLASS")?;
self.writer.write_string(1, &class.dxf_name)?;
self.writer.write_string(2, &class.cpp_class_name)?;
self.writer.write_string(3, &class.application_name)?;
self.writer.write_i32(90, class.proxy_flags.0 as i32)?;
self.writer.write_i32(91, class.instance_count)?;
self.writer.write_byte(280, if class.was_zombie { 1 } else { 0 })?;
self.writer.write_byte(281, if class.is_an_entity { 1 } else { 0 })?;
}
self.writer.write_section_end()?;
Ok(())
}
pub fn write_tables(&mut self, document: &CadDocument) -> Result<()> {
self.writer.write_section_start("TABLES")?;
self.write_vport_table(document)?;
self.write_ltype_table(document)?;
self.write_layer_table(document)?;
self.write_style_table(document)?;
self.write_view_table(document)?;
self.write_ucs_table(document)?;
self.write_appid_table(document)?;
self.write_dimstyle_table(document)?;
self.write_block_record_table(document)?;
self.writer.write_section_end()?;
Ok(())
}
fn write_vport_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.vports.handle();
self.write_table_header("VPORT", document.vports.len(), table_handle)?;
for vport in document.vports.iter() {
self.write_vport_entry(vport, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_vport_entry(&mut self, vport: &VPort, owner: Handle) -> Result<()> {
self.writer.write_string(0, "VPORT")?;
self.write_common_table_data(vport.handle(), owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbViewportTableRecord")?;
self.writer.write_string(2, vport.name())?;
self.writer.write_i16(70, 0)?;
self.writer.write_double(10, vport.lower_left.x)?;
self.writer.write_double(20, vport.lower_left.y)?;
self.writer.write_double(11, vport.upper_right.x)?;
self.writer.write_double(21, vport.upper_right.y)?;
self.writer.write_double(12, vport.view_center.x)?;
self.writer.write_double(22, vport.view_center.y)?;
self.writer.write_double(13, vport.snap_base.x)?;
self.writer.write_double(23, vport.snap_base.y)?;
self.writer.write_double(14, vport.snap_spacing.x)?;
self.writer.write_double(24, vport.snap_spacing.y)?;
self.writer.write_double(15, vport.grid_spacing.x)?;
self.writer.write_double(25, vport.grid_spacing.y)?;
self.writer.write_double(16, vport.view_direction.x)?;
self.writer.write_double(26, vport.view_direction.y)?;
self.writer.write_double(36, vport.view_direction.z)?;
self.writer.write_double(17, vport.view_target.x)?;
self.writer.write_double(27, vport.view_target.y)?;
self.writer.write_double(37, vport.view_target.z)?;
self.writer.write_double(40, vport.view_height)?;
self.writer.write_double(41, vport.aspect_ratio)?;
self.writer.write_double(42, vport.lens_length)?;
self.writer.write_double(43, vport.front_clip)?;
self.writer.write_double(44, vport.back_clip)?;
self.writer.write_double(50, vport.snap_rotation)?;
self.writer.write_double(51, vport.view_twist)?;
self.writer.write_i16(71, if vport.ucsfollow { 4 } else { 0 })?;
self.writer.write_i16(72, vport.circle_zoom)?;
self.writer.write_i16(73, if vport.fast_zoom { 1 } else { 0 })?;
self.writer.write_i16(74, 3)?;
self.writer.write_i16(75, if vport.snap_on { 1 } else { 0 })?;
self.writer.write_i16(76, if vport.grid_on { 1 } else { 0 })?;
self.writer.write_i16(77, if vport.snap_style { 1 } else { 0 })?;
self.writer.write_i16(78, vport.snap_isopair)?;
Ok(())
}
fn write_ltype_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.line_types.handle();
self.write_table_header("LTYPE", document.line_types.len(), table_handle)?;
for ltype in document.line_types.iter() {
self.write_ltype_entry(ltype, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_ltype_entry(&mut self, ltype: &LineType, owner: Handle) -> Result<()> {
self.writer.write_string(0, "LTYPE")?;
self.write_common_table_data(ltype.handle(), owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbLinetypeTableRecord")?;
self.writer.write_string(2, ltype.name())?;
let mut flags: i16 = 0;
if ltype.xref_dependent {
flags |= 0x10;
}
self.writer.write_i16(70, flags)?;
self.writer.write_string(3, <ype.description)?;
self.writer.write_i16(72, 65)?; self.writer.write_i16(73, ltype.elements.len() as i16)?;
self.writer.write_double(40, ltype.pattern_length)?;
for element in <ype.elements {
self.writer.write_double(49, element.length)?;
self.writer.write_i16(74, 0)?;
}
Ok(())
}
fn write_layer_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.layers.handle();
self.write_table_header("LAYER", document.layers.len(), table_handle)?;
for layer in document.layers.iter() {
self.write_layer_entry(layer, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_layer_entry(&mut self, layer: &Layer, owner: Handle) -> Result<()> {
self.writer.write_string(0, "LAYER")?;
self.write_common_table_data(layer.handle(), owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbLayerTableRecord")?;
self.writer.write_string(2, layer.name())?;
let mut flags: i16 = 0;
if layer.is_frozen() {
flags |= 1;
}
if layer.is_locked() {
flags |= 4;
}
if layer.flags.xref_dependent {
flags |= 0x10;
}
self.writer.write_i16(70, flags)?;
let color_index = match layer.color {
Color::Index(i) => i as i16,
Color::ByLayer => 7,
Color::ByBlock => 0,
Color::Rgb { .. } => 7,
};
if !layer.is_off() {
self.writer.write_i16(62, color_index)?;
} else {
self.writer.write_i16(62, -color_index)?;
}
self.writer.write_string(6, &layer.line_type)?;
self.writer.write_i16(370, layer.line_weight.value())?;
self.writer
.write_bool(290, layer.is_plottable)?;
Ok(())
}
fn write_style_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.text_styles.handle();
self.write_table_header("STYLE", document.text_styles.len(), table_handle)?;
for style in document.text_styles.iter() {
self.write_style_entry(style, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_style_entry(&mut self, style: &TextStyle, owner: Handle) -> Result<()> {
self.writer.write_string(0, "STYLE")?;
self.write_common_table_data(style.handle(), owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbTextStyleTableRecord")?;
self.writer.write_string(2, style.name())?;
let mut flags: i16 = 0;
if style.xref_dependent { flags |= 0x10; }
self.writer.write_i16(70, flags)?;
self.writer.write_double(40, style.height)?;
self.writer.write_double(41, style.width_factor)?;
self.writer.write_double(50, style.oblique_angle)?;
self.writer.write_i16(71, 0)?; self.writer.write_double(42, style.effective_last_height())?;
self.writer.write_string(3, &style.font_file)?;
self.writer.write_string(4, &style.big_font_file)?;
Ok(())
}
fn write_view_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.views.handle();
self.write_table_header("VIEW", document.views.len(), table_handle)?;
for view in document.views.iter() {
self.write_view_entry(view, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_view_entry(&mut self, view: &View, owner: Handle) -> Result<()> {
self.writer.write_string(0, "VIEW")?;
self.write_common_table_data(view.handle(), owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbViewTableRecord")?;
self.writer.write_string(2, view.name())?;
self.writer.write_i16(70, 0)?;
self.writer.write_double(40, view.height)?;
self.writer.write_double(10, view.center.x)?;
self.writer.write_double(20, view.center.y)?;
self.writer.write_double(41, view.width)?;
self.writer.write_double(11, view.direction.x)?;
self.writer.write_double(21, view.direction.y)?;
self.writer.write_double(31, view.direction.z)?;
self.writer.write_double(12, view.target.x)?;
self.writer.write_double(22, view.target.y)?;
self.writer.write_double(32, view.target.z)?;
self.writer.write_double(42, view.lens_length)?;
self.writer.write_double(43, view.front_clip)?;
self.writer.write_double(44, view.back_clip)?;
self.writer.write_double(50, view.twist_angle)?;
Ok(())
}
fn write_ucs_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.ucss.handle();
self.write_table_header("UCS", document.ucss.len(), table_handle)?;
for ucs in document.ucss.iter() {
self.write_ucs_entry(ucs, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_ucs_entry(&mut self, ucs: &Ucs, owner: Handle) -> Result<()> {
self.writer.write_string(0, "UCS")?;
self.write_common_table_data(ucs.handle(), owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbUCSTableRecord")?;
self.writer.write_string(2, ucs.name())?;
self.writer.write_i16(70, 0)?;
self.writer.write_double(10, ucs.origin.x)?;
self.writer.write_double(20, ucs.origin.y)?;
self.writer.write_double(30, ucs.origin.z)?;
self.writer.write_double(11, ucs.x_axis.x)?;
self.writer.write_double(21, ucs.x_axis.y)?;
self.writer.write_double(31, ucs.x_axis.z)?;
self.writer.write_double(12, ucs.y_axis.x)?;
self.writer.write_double(22, ucs.y_axis.y)?;
self.writer.write_double(32, ucs.y_axis.z)?;
Ok(())
}
fn write_appid_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.app_ids.handle();
self.write_table_header("APPID", document.app_ids.len(), table_handle)?;
for appid in document.app_ids.iter() {
self.write_appid_entry(appid, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_appid_entry(&mut self, appid: &AppId, owner: Handle) -> Result<()> {
self.writer.write_string(0, "APPID")?;
self.write_common_table_data(appid.handle(), owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbRegAppTableRecord")?;
self.writer.write_string(2, appid.name())?;
self.writer.write_i16(70, 0)?;
Ok(())
}
fn write_dimstyle_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.dim_styles.handle();
self.write_table_header("DIMSTYLE", document.dim_styles.len(), table_handle)?;
self.writer.write_subclass("AcDbDimStyleTable")?;
self.writer.write_i16(71, document.dim_styles.len() as i16)?;
for dimstyle in document.dim_styles.iter() {
self.write_dimstyle_entry(dimstyle, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_dimstyle_entry(&mut self, dimstyle: &DimStyle, owner: Handle) -> Result<()> {
self.writer.write_string(0, "DIMSTYLE")?;
self.writer.write_handle(105, dimstyle.handle())?;
self.writer.write_handle(330, owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbDimStyleTableRecord")?;
self.writer.write_string(2, dimstyle.name())?;
self.writer.write_i16(70, 0)?;
if !dimstyle.dimpost.is_empty() && dimstyle.dimpost != "<>" { self.writer.write_string(3, &dimstyle.dimpost)?; }
if !dimstyle.dimapost.is_empty() { self.writer.write_string(4, &dimstyle.dimapost)?; }
self.writer.write_double(40, dimstyle.dimscale)?;
self.writer.write_double(41, dimstyle.dimasz)?;
self.writer.write_double(42, dimstyle.dimexo)?;
self.writer.write_double(43, dimstyle.dimdli)?;
self.writer.write_double(44, dimstyle.dimexe)?;
self.writer.write_double(45, dimstyle.dimrnd)?;
self.writer.write_double(46, dimstyle.dimdle)?;
self.writer.write_double(47, dimstyle.dimtp)?;
self.writer.write_double(48, dimstyle.dimtm)?;
if dimstyle.dimfxl != 1.0 { self.writer.write_double(49, dimstyle.dimfxl)?; }
if dimstyle.dimjogang != std::f64::consts::FRAC_PI_4 { self.writer.write_double(50, dimstyle.dimjogang)?; }
self.writer.write_double(140, dimstyle.dimtxt)?;
self.writer.write_double(141, dimstyle.dimcen)?;
self.writer.write_double(142, dimstyle.dimtsz)?;
self.writer.write_double(143, dimstyle.dimaltf)?;
self.writer.write_double(144, dimstyle.dimlfac)?;
self.writer.write_double(145, dimstyle.dimtvp)?;
self.writer.write_double(146, dimstyle.dimtfac)?;
self.writer.write_double(147, dimstyle.dimgap)?;
if dimstyle.dimaltrnd != 0.0 { self.writer.write_double(148, dimstyle.dimaltrnd)?; }
if dimstyle.dimtfill != 0 { self.writer.write_i16(69, dimstyle.dimtfill)?; }
self.writer.write_i16(71, if dimstyle.dimtol { 1 } else { 0 })?;
self.writer.write_i16(72, if dimstyle.dimlim { 1 } else { 0 })?;
self.writer.write_i16(73, if dimstyle.dimtih { 1 } else { 0 })?;
self.writer.write_i16(74, if dimstyle.dimtoh { 1 } else { 0 })?;
self.writer.write_i16(75, if dimstyle.dimse1 { 1 } else { 0 })?;
self.writer.write_i16(76, if dimstyle.dimse2 { 1 } else { 0 })?;
self.writer.write_i16(77, dimstyle.dimtad)?;
self.writer.write_i16(78, dimstyle.dimzin)?;
self.writer.write_i16(79, dimstyle.dimazin)?;
if dimstyle.dimarcsym != 0 { self.writer.write_i32(90, dimstyle.dimarcsym as i32)?; }
self.writer.write_i16(170, if dimstyle.dimalt { 1 } else { 0 })?;
self.writer.write_i16(171, dimstyle.dimaltd)?;
self.writer.write_i16(172, if dimstyle.dimtofl { 1 } else { 0 })?;
self.writer.write_i16(173, if dimstyle.dimsah { 1 } else { 0 })?;
self.writer.write_i16(174, if dimstyle.dimtix { 1 } else { 0 })?;
self.writer.write_i16(175, if dimstyle.dimsoxd { 1 } else { 0 })?;
self.writer.write_i16(176, dimstyle.dimclrd)?;
self.writer.write_i16(177, dimstyle.dimclre)?;
self.writer.write_i16(178, dimstyle.dimclrt)?;
self.writer.write_i16(179, dimstyle.dimadec)?;
self.writer.write_i16(271, dimstyle.dimdec)?;
self.writer.write_i16(272, dimstyle.dimtdec)?;
self.writer.write_i16(273, dimstyle.dimaltu)?;
self.writer.write_i16(274, dimstyle.dimalttd)?;
self.writer.write_i16(275, dimstyle.dimaunit)?;
self.writer.write_i16(276, dimstyle.dimfrac)?;
self.writer.write_i16(277, dimstyle.dimlunit)?;
self.writer.write_i16(278, dimstyle.dimdsep)?;
self.writer.write_i16(279, dimstyle.dimtmove)?;
self.writer.write_i16(280, dimstyle.dimjust)?;
self.writer.write_i16(281, if dimstyle.dimsd1 { 1 } else { 0 })?;
self.writer.write_i16(282, if dimstyle.dimsd2 { 1 } else { 0 })?;
self.writer.write_i16(283, dimstyle.dimtolj)?;
self.writer.write_i16(284, dimstyle.dimtzin)?;
self.writer.write_i16(285, dimstyle.dimaltz)?;
self.writer.write_i16(286, dimstyle.dimalttz)?;
self.writer.write_i16(289, dimstyle.dimatfit)?;
if dimstyle.dimfxlon { self.writer.write_bool(290, true)?; }
if dimstyle.dimtxtdirection { self.writer.write_bool(295, true)?; }
if !dimstyle.dimtxsty_handle.is_null() { self.writer.write_handle(340, dimstyle.dimtxsty_handle)?; }
if !dimstyle.dimldrblk.is_null() { self.writer.write_handle(341, dimstyle.dimldrblk)?; }
if !dimstyle.dimblk.is_null() { self.writer.write_handle(342, dimstyle.dimblk)?; }
if !dimstyle.dimblk1.is_null() { self.writer.write_handle(343, dimstyle.dimblk1)?; }
if !dimstyle.dimblk2.is_null() { self.writer.write_handle(344, dimstyle.dimblk2)?; }
if !dimstyle.dimltex_handle.is_null() { self.writer.write_handle(345, dimstyle.dimltex_handle)?; }
if !dimstyle.dimltex1_handle.is_null() { self.writer.write_handle(346, dimstyle.dimltex1_handle)?; }
if !dimstyle.dimltex2_handle.is_null() { self.writer.write_handle(347, dimstyle.dimltex2_handle)?; }
self.writer.write_i16(371, dimstyle.dimlwd)?;
self.writer.write_i16(372, dimstyle.dimlwe)?;
Ok(())
}
fn write_block_record_table(&mut self, document: &CadDocument) -> Result<()> {
let table_handle = document.block_records.handle();
self.write_table_header("BLOCK_RECORD", document.block_records.len(), table_handle)?;
for block_record in document.block_records.iter() {
self.write_block_record_entry(block_record, table_handle)?;
}
self.write_table_end()?;
Ok(())
}
fn write_block_record_entry(&mut self, block_record: &BlockRecord, owner: Handle) -> Result<()> {
self.writer.write_string(0, "BLOCK_RECORD")?;
self.write_common_table_data(block_record.handle(), owner)?;
self.writer.write_subclass("AcDbSymbolTableRecord")?;
self.writer.write_subclass("AcDbBlockTableRecord")?;
self.writer.write_string(2, block_record.name())?;
self.writer.write_i16(70, block_record.units)?;
self.writer
.write_byte(280, if block_record.explodable { 1 } else { 0 })?;
self.writer.write_i16(
281,
if block_record.scale_uniformly { 1 } else { 0 },
)?;
Ok(())
}
fn write_table_header(&mut self, name: &str, count: usize, table_handle: Handle) -> Result<()> {
self.writer.write_string(0, "TABLE")?;
self.writer.write_string(2, name)?;
self.writer.write_handle(5, table_handle)?;
self.writer.write_handle(330, Handle::new(0))?; self.writer.write_subclass("AcDbSymbolTable")?;
self.writer.write_i16(70, count as i16)?;
Ok(())
}
fn write_table_end(&mut self) -> Result<()> {
self.writer.write_string(0, "ENDTAB")
}
fn write_common_table_data(&mut self, handle: Handle, owner: Handle) -> Result<()> {
self.writer.write_handle(5, handle)?;
self.writer.write_handle(330, owner)?;
Ok(())
}
pub fn write_blocks(&mut self, document: &CadDocument) -> Result<()> {
self.writer.write_section_start("BLOCKS")?;
for block_record in document.block_records.iter() {
self.write_block_definition(block_record, document)?;
}
self.writer.write_section_end()?;
Ok(())
}
fn write_block_definition(&mut self, block_record: &BlockRecord, document: &CadDocument) -> Result<()> {
let owner = block_record.handle();
let is_paper_space = block_record.name().starts_with("*Paper_Space");
let mut flags: i16 = 0;
let name = block_record.name();
let is_anonymous_block = block_record.flags.anonymous
|| (name.starts_with('*')
&& !name.starts_with("*Model_Space")
&& !name.starts_with("*Paper_Space"));
if is_anonymous_block {
flags |= 1; }
if block_record.flags.has_attributes {
flags |= 2; }
if block_record.flags.is_xref {
flags |= 4; }
if block_record.flags.is_xref_overlay {
flags |= 8; }
if block_record.flags.is_external {
flags |= 16; }
self.writer.write_string(0, "BLOCK")?;
self.writer.write_handle(5, block_record.block_entity_handle)?;
self.writer.write_handle(330, owner)?;
self.writer.write_subclass("AcDbEntity")?;
if is_paper_space {
self.writer.write_i16(67, 1)?;
}
self.writer.write_string(8, "0")?;
self.writer.write_subclass("AcDbBlockBegin")?;
self.writer.write_string(2, block_record.name())?;
self.writer.write_i16(70, flags)?;
self.writer.write_double(10, 0.0)?;
self.writer.write_double(20, 0.0)?;
self.writer.write_double(30, 0.0)?;
self.writer.write_string(3, block_record.name())?;
self.writer.write_string(1, &block_record.xref_path)?;
if !block_record.is_model_space() && block_record.name() != "*Paper_Space" {
let prev_ps = self.writing_paper_space;
if is_paper_space {
self.writing_paper_space = true;
}
for eh in &block_record.entity_handles {
if let Some(&idx) = document.entity_index.get(eh) {
self.write_entity_with_owner(&document.entities[idx], owner)?;
}
}
self.writing_paper_space = prev_ps;
}
self.writer.write_string(0, "ENDBLK")?;
self.writer.write_handle(5, block_record.block_end_handle)?;
self.writer.write_handle(330, owner)?;
self.writer.write_subclass("AcDbEntity")?;
if is_paper_space {
self.writer.write_i16(67, 1)?;
}
self.writer.write_string(8, "0")?;
self.writer.write_subclass("AcDbBlockEnd")?;
Ok(())
}
pub fn write_entities(&mut self, document: &CadDocument) -> Result<()> {
self.writer.write_section_start("ENTITIES")?;
self.writing_paper_space = false;
if let Some(model_space) = document.block_records.get("*Model_Space") {
let owner = model_space.handle();
for eh in &model_space.entity_handles {
if let Some(&idx) = document.entity_index.get(eh) {
self.write_entity_with_owner(&document.entities[idx], owner)?;
}
}
}
self.writing_paper_space = true;
if let Some(paper_space) = document.block_records.get("*Paper_Space") {
let owner = paper_space.handle();
for eh in &paper_space.entity_handles {
if let Some(&idx) = document.entity_index.get(eh) {
self.write_entity_with_owner(&document.entities[idx], owner)?;
}
}
}
self.writing_paper_space = false;
self.writer.write_section_end()?;
Ok(())
}
fn write_entity_with_owner(&mut self, entity: &EntityType, owner: Handle) -> Result<()> {
match entity {
EntityType::Point(e) => self.write_point(e, owner),
EntityType::Line(e) => self.write_line(e, owner),
EntityType::Circle(e) => self.write_circle(e, owner),
EntityType::Arc(e) => self.write_arc(e, owner),
EntityType::Ellipse(e) => self.write_ellipse(e, owner),
EntityType::Polyline(e) => self.write_polyline(e, owner),
EntityType::Polyline2D(e) => self.write_polyline2d(e, owner),
EntityType::Polyline3D(e) => self.write_polyline3d(e, owner),
EntityType::LwPolyline(e) => self.write_lwpolyline(e, owner),
EntityType::Text(e) => self.write_text(e, owner),
EntityType::MText(e) => self.write_mtext(e, owner),
EntityType::Spline(e) => self.write_spline(e, owner),
EntityType::Dimension(dim) => self.write_dimension(dim, owner),
EntityType::Hatch(e) => self.write_hatch(e, owner),
EntityType::Solid(e) => self.write_solid(e, owner),
EntityType::Face3D(e) => self.write_face3d(e, owner),
EntityType::Insert(e) => self.write_insert(e, owner),
EntityType::Block(e) => self.write_block_entity(e, owner),
EntityType::BlockEnd(e) => self.write_block_end(e, owner),
EntityType::Ray(e) => self.write_ray(e, owner),
EntityType::XLine(e) => self.write_xline(e, owner),
EntityType::Viewport(e) => self.write_viewport(e, owner),
EntityType::AttributeDefinition(e) => self.write_attdef(e, owner),
EntityType::AttributeEntity(e) => self.write_attrib(e, owner),
EntityType::Leader(e) => self.write_leader(e, owner),
EntityType::MultiLeader(e) => self.write_multileader(e, owner),
EntityType::MLine(e) => self.write_mline(e, owner),
EntityType::Mesh(e) => self.write_mesh(e, owner),
EntityType::RasterImage(e) => self.write_raster_image(e, owner),
EntityType::Solid3D(e) => self.write_solid3d(e, owner),
EntityType::Region(e) => self.write_region(e, owner),
EntityType::Body(e) => self.write_body(e, owner),
EntityType::Table(e) => self.write_acad_table(e, owner),
EntityType::Tolerance(e) => self.write_tolerance(e, owner),
EntityType::PolyfaceMesh(e) => self.write_polyface_mesh(e, owner),
EntityType::Wipeout(e) => self.write_wipeout(e, owner),
EntityType::Shape(e) => self.write_shape(e, owner),
EntityType::Underlay(e) => self.write_underlay(e, owner),
EntityType::Seqend(e) => self.write_seqend(e, owner),
EntityType::Ole2Frame(e) => self.write_ole2frame(e, owner),
EntityType::PolygonMesh(e) => self.write_polygon_mesh(e, owner),
EntityType::Unknown(e) => self.write_unknown_entity(e, owner),
}
}
fn write_common_entity_data(&mut self, common: &EntityCommon, owner: Handle) -> Result<()> {
self.writer.write_handle(5, common.handle)?;
self.writer.write_handle(330, owner)?;
if let Some(xdict) = common.xdictionary_handle {
if xdict != Handle::NULL && (self.valid_handles.is_empty() || self.valid_handles.contains(&xdict)) {
self.writer.write_string(102, "{ACAD_XDICTIONARY")?;
self.writer.write_handle(360, xdict)?;
self.writer.write_string(102, "}")?;
}
}
if !common.reactors.is_empty() {
let valid_reactors: Vec<Handle> = if self.valid_handles.is_empty() {
common.reactors.clone()
} else {
common.reactors.iter().copied()
.filter(|r| self.valid_handles.contains(r))
.collect()
};
if !valid_reactors.is_empty() {
self.writer.write_string(102, "{ACAD_REACTORS")?;
for reactor in &valid_reactors {
self.writer.write_handle(330, *reactor)?;
}
self.writer.write_string(102, "}")?;
}
}
self.writer.write_subclass("AcDbEntity")?;
if self.writing_paper_space {
self.writer.write_i16(67, 1)?;
}
self.writer.write_string(8, &common.layer)?;
if common.has_linetype() {
self.writer.write_string(6, &common.linetype)?;
}
if common.color != Color::ByLayer {
self.writer.write_color(62, common.color)?;
}
if self.dxf_version >= DxfVersion::AC1018 {
if let Some(tc) = common.color.to_true_color_value() {
self.writer.write_i32(420, tc)?;
}
}
if (common.linetype_scale - 1.0).abs() > 1e-12 {
self.writer.write_double(48, common.linetype_scale)?;
}
if common.line_weight != crate::types::LineWeight::ByLayer {
self.writer.write_i16(370, common.line_weight.value())?;
}
if common.invisible {
self.writer.write_i16(60, 1)?;
}
if self.dxf_version >= DxfVersion::AC1018 && !common.transparency.is_opaque() {
self.writer.write_i32(440, common.transparency.to_dxf_value())?;
}
Ok(())
}
fn write_normal(&mut self, normal: Vector3) -> Result<()> {
if normal != Vector3::UNIT_Z {
self.writer.write_double(210, normal.x)?;
self.writer.write_double(220, normal.y)?;
self.writer.write_double(230, normal.z)?;
}
Ok(())
}
fn write_unknown_entity(
&mut self,
entity: &crate::entities::UnknownEntity,
owner: Handle,
) -> Result<()> {
if let Some(ref codes) = entity.raw_dxf_codes {
self.writer.write_entity_type(&entity.dxf_name)?;
self.write_common_entity_data(&entity.common, owner)?;
for (code, value) in codes {
self.writer.write_string(*code, value)?;
}
Ok(())
} else {
Ok(())
}
}
fn write_point(&mut self, point: &Point, owner: Handle) -> Result<()> {
self.writer.write_entity_type("POINT")?;
self.write_common_entity_data(&point.common, owner)?;
self.writer.write_subclass("AcDbPoint")?;
self.writer.write_point3d(10, point.location)?;
if point.thickness != 0.0 {
self.writer.write_double(39, point.thickness)?;
}
self.write_normal(point.normal)?;
Ok(())
}
fn write_line(&mut self, line: &Line, owner: Handle) -> Result<()> {
self.writer.write_entity_type("LINE")?;
self.write_common_entity_data(&line.common, owner)?;
self.writer.write_subclass("AcDbLine")?;
self.writer.write_point3d(10, line.start)?;
self.writer.write_point3d(11, line.end)?;
if line.thickness != 0.0 {
self.writer.write_double(39, line.thickness)?;
}
self.write_normal(line.normal)?;
Ok(())
}
fn write_circle(&mut self, circle: &Circle, owner: Handle) -> Result<()> {
self.writer.write_entity_type("CIRCLE")?;
self.write_common_entity_data(&circle.common, owner)?;
self.writer.write_subclass("AcDbCircle")?;
self.writer.write_point3d(10, circle.center)?;
self.writer.write_double(40, circle.radius)?;
if circle.thickness != 0.0 {
self.writer.write_double(39, circle.thickness)?;
}
self.write_normal(circle.normal)?;
Ok(())
}
fn write_arc(&mut self, arc: &Arc, owner: Handle) -> Result<()> {
self.writer.write_entity_type("ARC")?;
self.write_common_entity_data(&arc.common, owner)?;
self.writer.write_subclass("AcDbCircle")?;
self.writer.write_point3d(10, arc.center)?;
self.writer.write_double(40, arc.radius)?;
if arc.thickness != 0.0 {
self.writer.write_double(39, arc.thickness)?;
}
self.write_normal(arc.normal)?;
self.writer.write_subclass("AcDbArc")?;
self.writer.write_double(50, arc.start_angle.to_degrees())?;
self.writer.write_double(51, arc.end_angle.to_degrees())?;
Ok(())
}
fn write_ellipse(&mut self, ellipse: &Ellipse, owner: Handle) -> Result<()> {
self.writer.write_entity_type("ELLIPSE")?;
self.write_common_entity_data(&ellipse.common, owner)?;
self.writer.write_subclass("AcDbEllipse")?;
self.writer.write_point3d(10, ellipse.center)?;
self.writer.write_point3d(11, ellipse.major_axis)?;
self.writer.write_double(40, ellipse.minor_axis_ratio)?;
self.write_normal(ellipse.normal)?;
self.writer.write_double(41, ellipse.start_parameter)?;
self.writer.write_double(42, ellipse.end_parameter)?;
Ok(())
}
fn write_polyline(&mut self, polyline: &Polyline, owner: Handle) -> Result<()> {
self.writer.write_entity_type("POLYLINE")?;
self.write_common_entity_data(&polyline.common, owner)?;
self.writer.write_subclass("AcDb3dPolyline")?;
self.writer.write_i16(66, 1)?;
self.writer.write_double(10, 0.0)?;
self.writer.write_double(20, 0.0)?;
self.writer.write_double(30, 0.0)?;
let mut flags: i16 = 8; if polyline.is_closed() {
flags |= 1;
}
self.writer.write_i16(70, flags)?;
let polyline_handle = polyline.common.handle;
for vertex in polyline.vertices.iter() {
let vertex_handle = self.allocate_handle();
self.writer.write_entity_type("VERTEX")?;
self.writer.write_handle(5, vertex_handle)?;
self.writer.write_handle(330, polyline_handle)?;
self.writer.write_string(8, &polyline.common.layer)?;
if polyline.common.color != Color::ByLayer {
self.writer.write_color(62, polyline.common.color)?;
}
self.writer.write_point3d(10, vertex.location)?;
self.writer.write_i16(70, 32)?; }
let seqend_handle = self.allocate_handle();
self.writer.write_entity_type("SEQEND")?;
self.writer.write_handle(5, seqend_handle)?;
self.writer.write_handle(330, polyline_handle)?;
self.writer.write_string(8, &polyline.common.layer)?;
Ok(())
}
fn write_polyline2d(&mut self, polyline: &Polyline2D, owner: Handle) -> Result<()> {
self.writer.write_entity_type("POLYLINE")?;
self.write_common_entity_data(&polyline.common, owner)?;
self.writer.write_subclass("AcDb2dPolyline")?;
self.writer.write_i16(66, 1)?;
self.writer.write_double(10, 0.0)?;
self.writer.write_double(20, 0.0)?;
self.writer.write_double(30, polyline.elevation)?;
self.writer.write_i16(70, polyline.flags.bits() as i16)?;
if polyline.thickness != 0.0 {
self.writer.write_double(39, polyline.thickness)?;
}
if polyline.start_width != 0.0 {
self.writer.write_double(40, polyline.start_width)?;
}
if polyline.end_width != 0.0 {
self.writer.write_double(41, polyline.end_width)?;
}
let polyline_handle = polyline.common.handle;
for vertex in polyline.vertices.iter() {
let vertex_handle = self.allocate_handle();
self.writer.write_entity_type("VERTEX")?;
self.writer.write_handle(5, vertex_handle)?;
self.writer.write_handle(330, polyline_handle)?;
self.writer.write_string(8, &polyline.common.layer)?;
if polyline.common.color != Color::ByLayer {
self.writer.write_color(62, polyline.common.color)?;
}
self.writer.write_point3d(10, vertex.location)?;
if vertex.start_width != 0.0 {
self.writer.write_double(40, vertex.start_width)?;
}
if vertex.end_width != 0.0 {
self.writer.write_double(41, vertex.end_width)?;
}
if vertex.bulge != 0.0 {
self.writer.write_double(42, vertex.bulge)?;
}
self.writer.write_i16(70, vertex.flags.bits() as i16)?;
}
let seqend_handle = self.allocate_handle();
self.writer.write_entity_type("SEQEND")?;
self.writer.write_handle(5, seqend_handle)?;
self.writer.write_handle(330, polyline_handle)?;
self.writer.write_string(8, &polyline.common.layer)?;
Ok(())
}
fn write_lwpolyline(&mut self, lwpoly: &LwPolyline, owner: Handle) -> Result<()> {
self.writer.write_entity_type("LWPOLYLINE")?;
self.write_common_entity_data(&lwpoly.common, owner)?;
self.writer.write_subclass("AcDbPolyline")?;
self.writer.write_i32(90, lwpoly.vertices.len() as i32)?;
let mut flags: i16 = 0;
if lwpoly.is_closed {
flags |= 1;
}
if lwpoly.plinegen {
flags |= 128;
}
self.writer.write_i16(70, flags)?;
self.writer.write_double(38, lwpoly.elevation)?;
if lwpoly.thickness != 0.0 {
self.writer.write_double(39, lwpoly.thickness)?;
}
if lwpoly.constant_width != 0.0 {
self.writer.write_double(43, lwpoly.constant_width)?;
}
for vertex in &lwpoly.vertices {
self.writer.write_double(10, vertex.location.x)?;
self.writer.write_double(20, vertex.location.y)?;
self.writer.write_double(40, vertex.start_width)?;
self.writer.write_double(41, vertex.end_width)?;
self.writer.write_double(42, vertex.bulge)?;
}
self.write_normal(lwpoly.normal)?;
Ok(())
}
fn write_text(&mut self, text: &Text, owner: Handle) -> Result<()> {
self.writer.write_entity_type("TEXT")?;
self.write_common_entity_data(&text.common, owner)?;
self.writer.write_subclass("AcDbText")?;
self.writer.write_point3d(10, text.insertion_point)?;
self.writer.write_double(40, text.height)?;
self.writer.write_string(1, &text.value)?;
if text.rotation != 0.0 {
self.writer.write_double(50, text.rotation.to_degrees())?;
}
if text.width_factor != 1.0 {
self.writer.write_double(41, text.width_factor)?;
}
if text.oblique_angle != 0.0 {
self.writer.write_double(51, text.oblique_angle)?;
}
self.writer.write_string(7, &text.style)?;
self.writer.write_i16(72, text.horizontal_alignment as i16)?;
if let Some(align_pt) = text.alignment_point {
self.writer.write_point3d(11, align_pt)?;
}
self.write_normal(text.normal)?;
self.writer.write_subclass("AcDbText")?;
self.writer.write_i16(73, text.vertical_alignment as i16)?;
Ok(())
}
fn write_mtext(&mut self, mtext: &MText, owner: Handle) -> Result<()> {
self.writer.write_entity_type("MTEXT")?;
self.write_common_entity_data(&mtext.common, owner)?;
self.writer.write_subclass("AcDbMText")?;
self.writer.write_point3d(10, mtext.insertion_point)?;
self.writer.write_double(40, mtext.height)?;
self.writer.write_double(41, mtext.rectangle_width)?;
self.writer.write_i16(71, mtext.attachment_point as i16)?;
self.writer.write_i16(72, mtext.drawing_direction as i16)?;
let sanitized;
let text: &str = if mtext.value.contains('\n') || mtext.value.contains('\r') {
sanitized = mtext.value.replace("\r\n", "\\P").replace('\r', "\\P").replace('\n', "\\P");
&sanitized
} else {
&mtext.value
};
if text.len() > 250 {
let mut remaining = text;
while remaining.len() > 250 {
let mut split_pos = 250;
while split_pos > 0 && !remaining.is_char_boundary(split_pos) {
split_pos -= 1;
}
if split_pos == 0 {
split_pos = remaining.len();
}
let (chunk, rest) = remaining.split_at(split_pos);
self.writer.write_string(3, chunk)?;
remaining = rest;
}
self.writer.write_string(1, remaining)?;
} else {
self.writer.write_string(1, text)?;
}
self.writer.write_string(7, &mtext.style)?;
if mtext.rotation != 0.0 {
self.writer.write_double(50, mtext.rotation.to_degrees())?;
}
self.writer.write_double(44, mtext.line_spacing_factor)?;
self.write_normal(mtext.normal)?;
Ok(())
}
fn write_spline(&mut self, spline: &Spline, owner: Handle) -> Result<()> {
self.writer.write_entity_type("SPLINE")?;
self.write_common_entity_data(&spline.common, owner)?;
self.writer.write_subclass("AcDbSpline")?;
self.write_normal(spline.normal)?;
let mut flags: i16 = 0;
if spline.flags.closed {
flags |= 1;
}
if spline.flags.periodic {
flags |= 2;
}
if spline.flags.rational {
flags |= 4;
}
self.writer.write_i16(70, flags)?;
self.writer.write_i16(71, spline.degree as i16)?;
self.writer.write_i16(72, spline.knots.len() as i16)?;
self.writer
.write_i16(73, spline.control_points.len() as i16)?;
self.writer.write_i16(74, spline.fit_points.len() as i16)?;
self.writer.write_double(42, 0.0000001)?;
self.writer.write_double(43, 0.0000001)?;
self.writer.write_double(44, 0.0000001)?;
for knot in &spline.knots {
self.writer.write_double(40, *knot)?;
}
for (i, point) in spline.control_points.iter().enumerate() {
self.writer.write_point3d(10, *point)?;
if spline.flags.rational {
let w = spline.weights.get(i).copied().unwrap_or(1.0);
self.writer.write_double(41, w)?;
}
}
for point in &spline.fit_points {
self.writer.write_point3d(11, *point)?;
}
Ok(())
}
fn write_dimension(&mut self, dimension: &Dimension, owner: Handle) -> Result<()> {
match dimension {
Dimension::Aligned(dim) => self.write_dimension_aligned(dim, owner),
Dimension::Linear(dim) => self.write_dimension_linear(dim, owner),
Dimension::Radius(dim) => self.write_dimension_radius(dim, owner),
Dimension::Diameter(dim) => self.write_dimension_diameter(dim, owner),
Dimension::Angular2Ln(dim) => self.write_dimension_angular_2line(dim, owner),
Dimension::Angular3Pt(dim) => self.write_dimension_angular_3point(dim, owner),
Dimension::Ordinate(dim) => self.write_dimension_ordinate(dim, owner),
}
}
fn write_dimension_base(&mut self, base: &DimensionBase, type_flags: i16, owner: Handle) -> Result<()> {
self.writer.write_handle(5, base.common.handle)?;
self.writer.write_handle(330, owner)?;
if let Some(xdict) = base.common.xdictionary_handle {
if xdict != Handle::NULL && (self.valid_handles.is_empty() || self.valid_handles.contains(&xdict)) {
self.writer.write_string(102, "{ACAD_XDICTIONARY")?;
self.writer.write_handle(360, xdict)?;
self.writer.write_string(102, "}")?;
}
}
if !base.common.reactors.is_empty() {
let valid_reactors: Vec<Handle> = if self.valid_handles.is_empty() {
base.common.reactors.clone()
} else {
base.common.reactors.iter().copied()
.filter(|r| self.valid_handles.contains(r))
.collect()
};
if !valid_reactors.is_empty() {
self.writer.write_string(102, "{ACAD_REACTORS")?;
for reactor in &valid_reactors {
self.writer.write_handle(330, *reactor)?;
}
self.writer.write_string(102, "}")?;
}
}
self.writer.write_subclass("AcDbEntity")?;
self.writer.write_string(8, &base.common.layer)?;
if base.common.color != Color::ByLayer {
self.writer.write_color(62, base.common.color)?;
}
if self.dxf_version >= DxfVersion::AC1018 {
if let Some(tc) = base.common.color.to_true_color_value() {
self.writer.write_i32(420, tc)?;
}
}
if self.dxf_version >= DxfVersion::AC1018 && !base.common.transparency.is_opaque() {
self.writer.write_i32(440, base.common.transparency.to_dxf_value())?;
}
self.writer.write_subclass("AcDbDimension")?;
self.writer.write_string(2, &base.block_name)?;
self.writer.write_point3d(10, base.definition_point)?;
self.writer.write_point3d(11, base.text_middle_point)?;
self.writer.write_i16(70, type_flags)?;
self.writer.write_double(53, base.text_rotation)?;
self.writer.write_string(3, &base.style_name)?;
if !base.text.is_empty() {
self.writer.write_string(1, &base.text)?;
}
Ok(())
}
fn write_dimension_aligned(&mut self, dim: &DimensionAligned, owner: Handle) -> Result<()> {
self.writer.write_entity_type("DIMENSION")?;
self.write_dimension_base(&dim.base, 1, owner)?; self.writer.write_subclass("AcDbAlignedDimension")?;
self.writer.write_point3d(13, dim.first_point)?;
self.writer.write_point3d(14, dim.second_point)?;
Ok(())
}
fn write_dimension_linear(&mut self, dim: &DimensionLinear, owner: Handle) -> Result<()> {
self.writer.write_entity_type("DIMENSION")?;
self.write_dimension_base(&dim.base, 0, owner)?; self.writer.write_subclass("AcDbAlignedDimension")?;
self.writer.write_point3d(13, dim.first_point)?;
self.writer.write_point3d(14, dim.second_point)?;
self.writer.write_double(50, dim.rotation)?;
self.writer.write_subclass("AcDbRotatedDimension")?;
Ok(())
}
fn write_dimension_radius(&mut self, dim: &DimensionRadius, owner: Handle) -> Result<()> {
self.writer.write_entity_type("DIMENSION")?;
self.write_dimension_base(&dim.base, 4, owner)?; self.writer.write_subclass("AcDbRadialDimension")?;
self.writer.write_point3d(15, dim.angle_vertex)?;
self.writer.write_double(40, dim.leader_length)?;
Ok(())
}
fn write_dimension_diameter(&mut self, dim: &DimensionDiameter, owner: Handle) -> Result<()> {
self.writer.write_entity_type("DIMENSION")?;
self.write_dimension_base(&dim.base, 3, owner)?; self.writer.write_subclass("AcDbDiametricDimension")?;
self.writer.write_point3d(15, dim.angle_vertex)?;
self.writer.write_double(40, dim.leader_length)?;
Ok(())
}
fn write_dimension_angular_2line(&mut self, dim: &DimensionAngular2Ln, owner: Handle) -> Result<()> {
self.writer.write_entity_type("DIMENSION")?;
self.write_dimension_base(&dim.base, 2, owner)?; self.writer.write_subclass("AcDb2LineAngularDimension")?;
self.writer.write_point3d(13, dim.first_point)?;
self.writer.write_point3d(14, dim.second_point)?;
self.writer.write_point3d(15, dim.angle_vertex)?;
self.writer.write_point3d(16, dim.definition_point)?;
Ok(())
}
fn write_dimension_angular_3point(&mut self, dim: &DimensionAngular3Pt, owner: Handle) -> Result<()> {
self.writer.write_entity_type("DIMENSION")?;
self.write_dimension_base(&dim.base, 5, owner)?; self.writer.write_subclass("AcDb3PointAngularDimension")?;
self.writer.write_point3d(13, dim.first_point)?;
self.writer.write_point3d(14, dim.second_point)?;
self.writer.write_point3d(15, dim.angle_vertex)?;
Ok(())
}
fn write_dimension_ordinate(&mut self, dim: &DimensionOrdinate, owner: Handle) -> Result<()> {
self.writer.write_entity_type("DIMENSION")?;
let type_flags = if dim.is_ordinate_type_x { 64 } else { 128 }; self.write_dimension_base(&dim.base, 6 | type_flags, owner)?;
self.writer.write_subclass("AcDbOrdinateDimension")?;
self.writer.write_point3d(13, dim.feature_location)?;
self.writer.write_point3d(14, dim.leader_endpoint)?;
Ok(())
}
fn write_hatch(&mut self, hatch: &Hatch, owner: Handle) -> Result<()> {
self.writer.write_entity_type("HATCH")?;
self.write_common_entity_data(&hatch.common, owner)?;
self.writer.write_subclass("AcDbHatch")?;
self.writer.write_double(10, 0.0)?;
self.writer.write_double(20, 0.0)?;
self.writer.write_double(30, hatch.elevation)?;
self.writer.write_double(210, hatch.normal.x)?;
self.writer.write_double(220, hatch.normal.y)?;
self.writer.write_double(230, hatch.normal.z)?;
self.writer.write_string(2, &hatch.pattern.name)?;
self.writer.write_i16(70, if hatch.is_solid { 1 } else { 0 })?;
let effective_associative = hatch.is_associative && hatch.paths.iter().any(|p| {
if self.valid_handles.is_empty() {
!p.boundary_handles.is_empty()
} else {
p.boundary_handles.iter().any(|h| *h != Handle::NULL && self.valid_handles.contains(h))
}
});
self.writer
.write_i16(71, if effective_associative { 1 } else { 0 })?;
self.writer.write_i32(91, hatch.paths.len() as i32)?;
for path in &hatch.paths {
self.write_hatch_boundary_path(path)?;
}
self.writer.write_i16(75, hatch.style as i16)?;
self.writer.write_i16(76, hatch.pattern_type as i16)?;
if !hatch.is_solid {
self.writer
.write_double(52, hatch.pattern_angle.to_degrees())?;
self.writer.write_double(41, hatch.pattern_scale)?;
self.writer.write_i16(77, if hatch.is_double { 1 } else { 0 })?;
self.writer
.write_i16(78, hatch.pattern.lines.len() as i16)?;
for line in &hatch.pattern.lines {
self.writer.write_double(53, line.angle.to_degrees())?;
self.writer.write_double(43, line.base_point.x)?;
self.writer.write_double(44, line.base_point.y)?;
self.writer.write_double(45, line.offset.x)?;
self.writer.write_double(46, line.offset.y)?;
self.writer.write_i16(79, line.dash_lengths.len() as i16)?;
for dash in &line.dash_lengths {
self.writer.write_double(49, *dash)?;
}
}
}
self.writer.write_i32(98, hatch.seed_points.len() as i32)?;
for seed in &hatch.seed_points {
self.writer.write_double(10, seed.x)?;
self.writer.write_double(20, seed.y)?;
}
Ok(())
}
fn write_hatch_boundary_path(&mut self, path: &BoundaryPath) -> Result<()> {
self.writer.write_i32(92, get_boundary_path_bits(&path.flags) as i32)?;
if !path.flags.is_polyline() {
self.writer.write_i32(93, path.edges.len() as i32)?;
}
for edge in &path.edges {
self.write_hatch_edge(edge)?;
}
let valid_boundary_handles: Vec<Handle> = if self.valid_handles.is_empty() {
path.boundary_handles.clone()
} else {
path.boundary_handles.iter().copied()
.filter(|h| *h != Handle::NULL && self.valid_handles.contains(h))
.collect()
};
self.writer.write_i32(97, valid_boundary_handles.len() as i32)?;
for h in &valid_boundary_handles {
self.writer.write_handle(330, *h)?;
}
Ok(())
}
fn write_hatch_edge(&mut self, edge: &BoundaryEdge) -> Result<()> {
match edge {
BoundaryEdge::Line(line_edge) => {
self.writer.write_i16(72, 1)?; self.writer.write_double(10, line_edge.start.x)?;
self.writer.write_double(20, line_edge.start.y)?;
self.writer.write_double(11, line_edge.end.x)?;
self.writer.write_double(21, line_edge.end.y)?;
}
BoundaryEdge::CircularArc(arc) => {
self.writer.write_i16(72, 2)?; self.writer.write_double(10, arc.center.x)?;
self.writer.write_double(20, arc.center.y)?;
self.writer.write_double(40, arc.radius)?;
self.writer
.write_double(50, arc.start_angle.to_degrees())?;
self.writer.write_double(51, arc.end_angle.to_degrees())?;
self.writer
.write_i16(73, if arc.counter_clockwise { 1 } else { 0 })?;
}
BoundaryEdge::EllipticArc(ellipse) => {
self.writer.write_i16(72, 3)?; self.writer.write_double(10, ellipse.center.x)?;
self.writer.write_double(20, ellipse.center.y)?;
self.writer.write_double(11, ellipse.major_axis_endpoint.x)?;
self.writer.write_double(21, ellipse.major_axis_endpoint.y)?;
self.writer.write_double(40, ellipse.minor_axis_ratio)?;
self.writer.write_double(50, ellipse.start_angle)?;
self.writer.write_double(51, ellipse.end_angle)?;
self.writer
.write_i16(73, if ellipse.counter_clockwise { 1 } else { 0 })?;
}
BoundaryEdge::Spline(spline) => {
self.writer.write_i16(72, 4)?; self.writer
.write_i16(73, if spline.rational { 1 } else { 0 })?;
self.writer
.write_i16(74, if spline.periodic { 1 } else { 0 })?;
self.writer.write_i32(94, spline.degree)?;
self.writer.write_i32(95, spline.knots.len() as i32)?;
self.writer
.write_i32(96, spline.control_points.len() as i32)?;
for knot in &spline.knots {
self.writer.write_double(40, *knot)?;
}
for point in &spline.control_points {
self.writer.write_double(10, point.x)?;
self.writer.write_double(20, point.y)?;
if spline.rational {
self.writer.write_double(42, point.z)?; }
}
if self.dxf_version >= DxfVersion::AC1024 {
self.writer.write_i32(97, spline.fit_points.len() as i32)?;
for fp in &spline.fit_points {
self.writer.write_double(11, fp.x)?;
self.writer.write_double(21, fp.y)?;
}
if !spline.fit_points.is_empty() {
self.writer.write_double(12, spline.start_tangent.x)?;
self.writer.write_double(22, spline.start_tangent.y)?;
self.writer.write_double(13, spline.end_tangent.x)?;
self.writer.write_double(23, spline.end_tangent.y)?;
}
}
}
BoundaryEdge::Polyline(poly) => {
let has_bulge = poly.has_bulge();
self.writer.write_i16(72, if has_bulge { 1 } else { 0 })?;
self.writer
.write_i16(73, if poly.is_closed { 1 } else { 0 })?;
self.writer.write_i32(93, poly.vertices.len() as i32)?;
for vertex in &poly.vertices {
self.writer.write_double(10, vertex.x)?;
self.writer.write_double(20, vertex.y)?;
if has_bulge {
self.writer.write_double(42, vertex.z)?; }
}
}
}
Ok(())
}
fn write_solid(&mut self, solid: &Solid, owner: Handle) -> Result<()> {
self.writer.write_entity_type("SOLID")?;
self.write_common_entity_data(&solid.common, owner)?;
self.writer.write_subclass("AcDbTrace")?;
self.writer.write_point3d(10, solid.first_corner)?;
self.writer.write_point3d(11, solid.second_corner)?;
self.writer.write_point3d(12, solid.third_corner)?;
self.writer.write_point3d(13, solid.fourth_corner)?;
if solid.thickness != 0.0 {
self.writer.write_double(39, solid.thickness)?;
}
self.write_normal(solid.normal)?;
Ok(())
}
fn write_face3d(&mut self, face: &Face3D, owner: Handle) -> Result<()> {
self.writer.write_entity_type("3DFACE")?;
self.write_common_entity_data(&face.common, owner)?;
self.writer.write_subclass("AcDbFace")?;
self.writer.write_point3d(10, face.first_corner)?;
self.writer.write_point3d(11, face.second_corner)?;
self.writer.write_point3d(12, face.third_corner)?;
self.writer.write_point3d(13, face.fourth_corner)?;
if face.invisible_edges != InvisibleEdgeFlags::NONE {
let edge_bits = get_invisible_edge_bits(&face.invisible_edges);
self.writer.write_i16(70, edge_bits as i16)?;
}
Ok(())
}
fn write_insert(&mut self, insert: &Insert, owner: Handle) -> Result<()> {
self.writer.write_entity_type("INSERT")?;
self.write_common_entity_data(&insert.common, owner)?;
self.writer.write_subclass(insert.subclass_marker())?;
if insert.has_attributes() {
self.writer.write_i16(66, 1)?;
}
self.writer.write_string(2, &insert.block_name)?;
self.writer.write_point3d(10, insert.insert_point)?;
if insert.x_scale() != 1.0 {
self.writer.write_double(41, insert.x_scale())?;
}
if insert.y_scale() != 1.0 {
self.writer.write_double(42, insert.y_scale())?;
}
if insert.z_scale() != 1.0 {
self.writer.write_double(43, insert.z_scale())?;
}
if insert.rotation != 0.0 {
self.writer.write_double(50, insert.rotation.to_degrees())?;
}
if insert.column_count > 1 {
self.writer.write_i16(70, insert.column_count as i16)?;
}
if insert.row_count > 1 {
self.writer.write_i16(71, insert.row_count as i16)?;
}
if insert.column_spacing != 0.0 {
self.writer.write_double(44, insert.column_spacing)?;
}
if insert.row_spacing != 0.0 {
self.writer.write_double(45, insert.row_spacing)?;
}
self.write_normal(insert.normal)?;
if insert.has_attributes() {
let insert_handle = insert.handle();
for att in &insert.attributes {
self.write_attrib(att, insert_handle)?;
}
let seqend_handle = self.allocate_handle();
self.writer.write_entity_type("SEQEND")?;
self.writer.write_handle(5, seqend_handle)?;
self.writer.write_handle(330, insert_handle)?;
self.writer.write_string(8, &insert.common.layer)?;
}
Ok(())
}
fn write_block_entity(&mut self, block: &Block, owner: Handle) -> Result<()> {
self.writer.write_entity_type("BLOCK")?;
self.write_common_entity_data(&block.common, owner)?;
self.writer.write_subclass("AcDbBlockBegin")?;
self.writer.write_string(2, &block.name)?;
self.writer.write_i16(70, 0)?; self.writer.write_point3d(10, block.base_point)?;
self.writer.write_string(3, &block.name)?;
if !block.xref_path.is_empty() {
self.writer.write_string(1, &block.xref_path)?;
}
if !block.description.is_empty() {
self.writer.write_string(4, &block.description)?;
}
Ok(())
}
fn write_block_end(&mut self, block_end: &BlockEnd, owner: Handle) -> Result<()> {
self.writer.write_entity_type("ENDBLK")?;
self.write_common_entity_data(&block_end.common, owner)?;
self.writer.write_subclass("AcDbBlockEnd")?;
Ok(())
}
fn write_ray(&mut self, ray: &Ray, owner: Handle) -> Result<()> {
self.writer.write_entity_type("RAY")?;
self.write_common_entity_data(&ray.common, owner)?;
self.writer.write_subclass("AcDbRay")?;
self.writer.write_point3d(10, ray.base_point)?;
self.writer.write_point3d(11, ray.direction)?;
Ok(())
}
fn write_xline(&mut self, xline: &XLine, owner: Handle) -> Result<()> {
self.writer.write_entity_type("XLINE")?;
self.write_common_entity_data(&xline.common, owner)?;
self.writer.write_subclass("AcDbXline")?;
self.writer.write_point3d(10, xline.base_point)?;
self.writer.write_point3d(11, xline.direction)?;
Ok(())
}
fn write_polyline3d(&mut self, polyline: &Polyline3D, owner: Handle) -> Result<()> {
self.writer.write_entity_type("POLYLINE")?;
self.write_common_entity_data(&polyline.common, owner)?;
self.writer.write_subclass("AcDb3dPolyline")?;
self.writer.write_i16(66, 1)?;
self.writer.write_double(10, 0.0)?;
self.writer.write_double(20, 0.0)?;
self.writer.write_double(30, polyline.elevation)?;
self.writer.write_i16(70, polyline.flags.to_bits() as i16)?;
let polyline_handle = polyline.handle();
for vertex in polyline.vertices.iter() {
let vertex_handle = if vertex.handle.is_null() {
self.allocate_handle()
} else {
vertex.handle
};
self.writer.write_entity_type("VERTEX")?;
self.writer.write_handle(5, vertex_handle)?;
self.writer.write_handle(330, polyline_handle)?;
self.writer.write_string(8, &vertex.layer)?;
if polyline.common.color != Color::ByLayer {
self.writer.write_color(62, polyline.common.color)?;
}
self.writer.write_point3d(10, vertex.position)?;
self.writer.write_i16(70, vertex.flags as i16)?;
}
self.writer.write_entity_type("SEQEND")?;
let seqend_handle = self.allocate_handle();
self.writer.write_handle(5, seqend_handle)?;
self.writer.write_handle(330, polyline_handle)?;
self.writer.write_string(8, &polyline.common.layer)?;
Ok(())
}
fn write_viewport(&mut self, viewport: &Viewport, owner: Handle) -> Result<()> {
self.writer.write_entity_type("VIEWPORT")?;
self.write_common_entity_data(&viewport.common, owner)?;
self.writer.write_subclass("AcDbViewport")?;
self.writer.write_point3d(10, viewport.center)?;
self.writer.write_double(40, viewport.width)?;
self.writer.write_double(41, viewport.height)?;
self.writer.write_i16(68, viewport.id)?;
self.writer.write_i32(90, viewport.status.to_bits())?;
self.writer.write_double(12, viewport.view_center.x)?;
self.writer.write_double(22, viewport.view_center.y)?;
self.writer.write_double(13, viewport.snap_base.x)?;
self.writer.write_double(23, viewport.snap_base.y)?;
self.writer.write_double(14, viewport.snap_spacing.x)?;
self.writer.write_double(24, viewport.snap_spacing.y)?;
self.writer.write_double(15, viewport.grid_spacing.x)?;
self.writer.write_double(25, viewport.grid_spacing.y)?;
self.writer.write_double(16, viewport.view_direction.x)?;
self.writer.write_double(26, viewport.view_direction.y)?;
self.writer.write_double(36, viewport.view_direction.z)?;
self.writer.write_double(17, viewport.view_target.x)?;
self.writer.write_double(27, viewport.view_target.y)?;
self.writer.write_double(37, viewport.view_target.z)?;
self.writer.write_double(42, viewport.lens_length)?;
self.writer.write_double(43, viewport.front_clip_z)?;
self.writer.write_double(44, viewport.back_clip_z)?;
self.writer.write_double(45, viewport.view_height)?;
self.writer.write_double(50, viewport.snap_angle)?;
self.writer.write_double(51, viewport.twist_angle)?;
self.writer.write_i16(72, viewport.circle_sides)?;
for frozen_layer in &viewport.frozen_layers {
if !frozen_layer.is_null() {
self.writer.write_handle(331, *frozen_layer)?;
}
}
self.writer.write_byte(281, viewport.render_mode.to_value() as u8)?;
if viewport.ucs_per_viewport {
self.writer.write_i16(71, 1)?;
}
if viewport.ucs_origin != Vector3::ZERO {
self.writer.write_double(110, viewport.ucs_origin.x)?;
self.writer.write_double(120, viewport.ucs_origin.y)?;
self.writer.write_double(130, viewport.ucs_origin.z)?;
}
if viewport.ucs_x_axis != Vector3::ZERO {
self.writer.write_double(111, viewport.ucs_x_axis.x)?;
self.writer.write_double(121, viewport.ucs_x_axis.y)?;
self.writer.write_double(131, viewport.ucs_x_axis.z)?;
}
if viewport.ucs_y_axis != Vector3::ZERO {
self.writer.write_double(112, viewport.ucs_y_axis.x)?;
self.writer.write_double(122, viewport.ucs_y_axis.y)?;
self.writer.write_double(132, viewport.ucs_y_axis.z)?;
}
if viewport.elevation != 0.0 {
self.writer.write_double(146, viewport.elevation)?;
}
if viewport.grid_major != 0 {
self.writer.write_i16(61, viewport.grid_major)?;
}
Ok(())
}
fn write_attdef(&mut self, attdef: &AttributeDefinition, owner: Handle) -> Result<()> {
self.writer.write_entity_type("ATTDEF")?;
self.write_common_entity_data(&attdef.common, owner)?;
self.writer.write_subclass("AcDbText")?;
self.writer.write_point3d(10, attdef.insertion_point)?;
self.writer.write_double(40, attdef.height)?;
self.writer.write_string(1, &attdef.default_value)?;
self.writer.write_double(50, attdef.rotation.to_degrees())?;
self.writer.write_double(41, attdef.width_factor)?;
self.writer.write_double(51, attdef.oblique_angle.to_degrees())?;
self.writer.write_string(7, &attdef.text_style)?;
self.writer.write_i16(71, attdef.text_generation_flags)?;
self.writer.write_i16(72, attdef.horizontal_alignment.to_value())?;
self.writer.write_point3d(11, attdef.alignment_point)?;
self.writer.write_point3d(210, attdef.normal)?;
self.writer.write_subclass("AcDbAttributeDefinition")?;
self.writer.write_string(2, &attdef.tag)?;
self.writer.write_i16(70, attdef.flags.to_bits() as i16)?;
self.writer.write_i16(73, attdef.field_length)?;
self.writer.write_i16(74, attdef.vertical_alignment.to_value())?;
self.writer.write_string(3, &attdef.prompt)?;
Ok(())
}
fn write_attrib(&mut self, attrib: &AttributeEntity, owner: Handle) -> Result<()> {
self.writer.write_entity_type("ATTRIB")?;
self.write_common_entity_data(&attrib.common, owner)?;
self.writer.write_subclass("AcDbText")?;
self.writer.write_point3d(10, attrib.insertion_point)?;
self.writer.write_double(40, attrib.height)?;
self.writer.write_string(1, &attrib.value)?;
self.writer.write_double(50, attrib.rotation.to_degrees())?;
self.writer.write_double(41, attrib.width_factor)?;
self.writer.write_double(51, attrib.oblique_angle.to_degrees())?;
self.writer.write_string(7, &attrib.text_style)?;
self.writer.write_i16(71, attrib.text_generation_flags)?;
self.writer.write_i16(72, attrib.horizontal_alignment.to_value())?;
self.writer.write_point3d(11, attrib.alignment_point)?;
self.writer.write_point3d(210, attrib.normal)?;
self.writer.write_subclass("AcDbAttribute")?;
self.writer.write_string(2, &attrib.tag)?;
self.writer.write_i16(70, attrib.flags.to_bits() as i16)?;
self.writer.write_i16(73, attrib.field_length)?;
self.writer.write_i16(74, attrib.vertical_alignment.to_value())?;
Ok(())
}
fn write_leader(&mut self, leader: &Leader, owner: Handle) -> Result<()> {
self.writer.write_entity_type("LEADER")?;
self.write_common_entity_data(&leader.common, owner)?;
self.writer.write_subclass("AcDbLeader")?;
self.writer.write_string(3, &leader.dimension_style)?;
self.writer.write_i16(71, if leader.arrow_enabled { 1 } else { 0 })?;
self.writer.write_i16(72, leader.path_type.to_value())?;
self.writer.write_i16(73, leader.creation_type.to_value())?;
self.writer.write_i16(74, leader.hookline_direction.to_value())?;
self.writer.write_i16(75, if leader.hookline_enabled { 1 } else { 0 })?;
self.writer.write_double(40, leader.text_height)?;
self.writer.write_double(41, leader.text_width)?;
self.writer.write_i16(76, leader.vertices.len() as i16)?;
for vertex in &leader.vertices {
self.writer.write_point3d(10, *vertex)?;
}
self.writer.write_point3d(210, leader.normal)?;
self.writer.write_point3d(211, leader.horizontal_direction)?;
self.writer.write_point3d(212, leader.block_offset)?;
self.writer.write_point3d(213, leader.annotation_offset)?;
Ok(())
}
pub fn write_objects(&mut self, document: &CadDocument) -> Result<()> {
self.writer.write_section_start("OBJECTS")?;
let mut root_handle = document.header.named_objects_dict_handle;
if root_handle.is_null()
|| !matches!(document.objects.get(&root_handle), Some(ObjectType::Dictionary(_)))
{
root_handle = Self::find_root_dict_handle(&document.objects);
}
if let Some(ObjectType::Dictionary(root_dict)) = document.objects.get(&root_handle) {
self.write_dictionary(root_dict, &document.objects)?;
}
for (handle, object) in document.objects.iter() {
if *handle == root_handle {
continue;
}
let object = object;
match object {
ObjectType::Dictionary(dict) => self.write_dictionary(dict, &document.objects)?,
ObjectType::Layout(layout) => self.write_layout(layout)?,
ObjectType::XRecord(xrecord) => self.write_xrecord(xrecord)?,
ObjectType::Group(group) => self.write_group(group)?,
ObjectType::MLineStyle(mlinestyle) => self.write_mlinestyle(mlinestyle)?,
ObjectType::ImageDefinition(imagedef) => self.write_image_definition(imagedef)?,
ObjectType::PlotSettings(plotsettings) => self.write_plot_settings(plotsettings)?,
ObjectType::MultiLeaderStyle(style) => self.write_multileader_style(style)?,
ObjectType::TableStyle(style) => self.write_table_style(style)?,
ObjectType::Scale(scale) => self.write_scale(scale)?,
ObjectType::SortEntitiesTable(table) => self.write_sort_entities_table(table)?,
ObjectType::DictionaryVariable(var) => self.write_dictionary_variable(var)?,
ObjectType::VisualStyle(obj) => self.write_visualstyle(obj)?,
ObjectType::Material(obj) => self.write_material(obj)?,
ObjectType::ImageDefinitionReactor(obj) => self.write_imagedef_reactor(obj)?,
ObjectType::GeoData(obj) => self.write_stub_handle_only("GEODATA", obj.handle, obj.owner)?,
ObjectType::SpatialFilter(obj) => self.write_stub_handle_only("SPATIAL_FILTER", obj.handle, obj.owner)?,
ObjectType::RasterVariables(obj) => self.write_raster_variables(obj)?,
ObjectType::BookColor(obj) => self.write_bookcolor(obj)?,
ObjectType::PlaceHolder(obj) => self.write_stub_handle_only("ACDBPLACEHOLDER", obj.handle, obj.owner)?,
ObjectType::DictionaryWithDefault(obj) => self.write_dict_with_default(obj, &document.objects)?,
ObjectType::WipeoutVariables(obj) => self.write_wipeout_variables(obj)?,
ObjectType::Unknown { type_name, handle, owner, raw_dxf_codes } => {
self.write_unknown_object(type_name, *handle, *owner, raw_dxf_codes.as_deref())?;
}
}
}
self.writer.write_section_end()?;
Ok(())
}
fn find_root_dict_handle(objects: &std::collections::HashMap<Handle, ObjectType>) -> Handle {
let mut best = Handle::NULL;
let mut best_count = 0usize;
for (handle, obj) in objects {
if let ObjectType::Dictionary(dict) = obj {
if dict.owner.is_null() {
if dict.entries.len() > best_count
|| (dict.entries.len() == best_count && handle.value() > best.value())
{
best = *handle;
best_count = dict.entries.len();
}
}
}
}
best
}
fn write_dictionary(&mut self, dict: &Dictionary, objects: &std::collections::HashMap<Handle, ObjectType>) -> Result<()> {
self.writer.write_string(0, "DICTIONARY")?;
self.writer.write_handle(5, dict.handle)?;
self.writer.write_handle(330, dict.owner)?;
self.writer.write_subclass("AcDbDictionary")?;
self.writer
.write_byte(280, if dict.hard_owner { 1 } else { 0 })?;
self.writer.write_byte(281, dict.duplicate_cloning as u8)?;
for (key, handle) in &dict.entries {
if !objects.contains_key(handle) {
continue;
}
self.writer.write_string(3, key)?;
self.writer.write_handle(350, *handle)?;
}
Ok(())
}
fn write_layout(&mut self, layout: &Layout) -> Result<()> {
self.writer.write_string(0, "LAYOUT")?;
self.writer.write_handle(5, layout.handle)?;
if let Some(xdict) = layout.xdictionary_handle {
if xdict != Handle::NULL && (self.valid_handles.is_empty() || self.valid_handles.contains(&xdict)) {
self.writer.write_string(102, "{ACAD_XDICTIONARY")?;
self.writer.write_handle(360, xdict)?;
self.writer.write_string(102, "}")?;
}
}
if !layout.reactors.is_empty() {
let valid_reactors: Vec<Handle> = if self.valid_handles.is_empty() {
layout.reactors.clone()
} else {
layout.reactors.iter().copied()
.filter(|r| self.valid_handles.contains(r))
.collect()
};
if !valid_reactors.is_empty() {
self.writer.write_string(102, "{ACAD_REACTORS")?;
for r in &valid_reactors {
self.writer.write_handle(330, *r)?;
}
self.writer.write_string(102, "}")?;
}
}
self.writer.write_handle(330, layout.owner)?;
self.writer.write_subclass("AcDbPlotSettings")?;
if let Some(ref codes) = layout.raw_plot_settings_codes {
for (code, value) in codes {
self.writer.write_string(*code, value)?;
}
} else {
self.writer.write_string(1, "")?; self.writer.write_string(2, "")?; self.writer.write_string(4, "")?; self.writer.write_string(6, "")?; self.writer.write_double(40, 0.0)?; self.writer.write_double(41, 0.0)?; self.writer.write_double(42, 0.0)?; self.writer.write_double(43, 0.0)?; self.writer.write_double(44, 0.0)?; self.writer.write_double(45, 0.0)?; self.writer.write_double(46, 0.0)?; self.writer.write_double(47, 0.0)?; self.writer.write_double(48, 0.0)?; self.writer.write_double(49, 0.0)?; self.writer.write_double(140, 0.0)?; self.writer.write_double(141, 0.0)?; self.writer.write_double(142, 1.0)?; self.writer.write_double(143, 1.0)?; let plot_flags: i16 = if layout.name == "Model" { 1024 } else { 0 };
self.writer.write_i16(70, plot_flags)?;
self.writer.write_i16(72, 0)?; self.writer.write_i16(73, 0)?; self.writer.write_i16(74, 0)?; }
self.writer.write_subclass("AcDbLayout")?;
self.writer.write_string(1, &layout.name)?;
self.writer.write_i16(70, layout.flags)?;
self.writer.write_i16(71, layout.tab_order)?;
self.writer.write_double(10, layout.min_limits.0)?;
self.writer.write_double(20, layout.min_limits.1)?;
self.writer.write_double(11, layout.max_limits.0)?;
self.writer.write_double(21, layout.max_limits.1)?;
self.writer.write_double(12, layout.insertion_base.0)?;
self.writer.write_double(22, layout.insertion_base.1)?;
self.writer.write_double(32, layout.insertion_base.2)?;
self.writer.write_double(14, layout.min_extents.0)?;
self.writer.write_double(24, layout.min_extents.1)?;
self.writer.write_double(34, layout.min_extents.2)?;
self.writer.write_double(15, layout.max_extents.0)?;
self.writer.write_double(25, layout.max_extents.1)?;
self.writer.write_double(35, layout.max_extents.2)?;
self.writer.write_double(146, layout.elevation)?;
self.writer.write_double(13, layout.ucs_origin.0)?;
self.writer.write_double(23, layout.ucs_origin.1)?;
self.writer.write_double(33, layout.ucs_origin.2)?;
self.writer.write_double(16, layout.ucs_x_axis.0)?;
self.writer.write_double(26, layout.ucs_x_axis.1)?;
self.writer.write_double(36, layout.ucs_x_axis.2)?;
self.writer.write_double(17, layout.ucs_y_axis.0)?;
self.writer.write_double(27, layout.ucs_y_axis.1)?;
self.writer.write_double(37, layout.ucs_y_axis.2)?;
self.writer.write_i16(76, layout.ucs_ortho_type)?;
self.writer.write_handle(330, layout.block_record)?;
if layout.viewport != Handle::NULL {
self.writer.write_handle(331, layout.viewport)?;
}
Ok(())
}
fn write_xrecord(&mut self, xrecord: &XRecord) -> Result<()> {
use crate::objects::XRecordValue;
self.writer.write_string(0, "XRECORD")?;
self.writer.write_handle(5, xrecord.handle)?;
self.writer.write_handle(330, xrecord.owner)?;
self.writer.write_subclass("AcDbXrecord")?;
self.writer.write_byte(280, xrecord.cloning_flags.to_code() as u8)?;
for entry in xrecord.iter() {
match &entry.value {
XRecordValue::String(s) => {
self.writer.write_string(entry.code, s)?;
}
XRecordValue::Double(d) => {
self.writer.write_double(entry.code, *d)?;
}
XRecordValue::Int16(i) => {
self.writer.write_i16(entry.code, *i)?;
}
XRecordValue::Int32(i) => {
self.writer.write_i32(entry.code, *i)?;
}
XRecordValue::Int64(i) => {
self.writer.write_i32(entry.code, *i as i32)?;
}
XRecordValue::Byte(b) => {
self.writer.write_i16(entry.code, *b as i16)?;
}
XRecordValue::Bool(b) => {
self.writer.write_i16(entry.code, if *b { 1 } else { 0 })?;
}
XRecordValue::Handle(h) => {
self.writer.write_handle(entry.code, *h)?;
}
XRecordValue::Point3D(x, y, z) => {
self.writer.write_double(entry.code, *x)?;
self.writer.write_double(entry.code + 10, *y)?;
self.writer.write_double(entry.code + 20, *z)?;
}
XRecordValue::Chunk(data) => {
self.writer.write_binary(entry.code, data)?;
}
}
}
Ok(())
}
fn write_group(&mut self, group: &Group) -> Result<()> {
self.writer.write_string(0, "GROUP")?;
self.writer.write_handle(5, group.handle)?;
self.writer.write_handle(330, group.owner)?;
self.writer.write_subclass("AcDbGroup")?;
self.writer.write_string(300, &group.description)?;
self.writer
.write_i16(70, if group.is_unnamed() { 1 } else { 0 })?;
self.writer
.write_i16(71, if group.selectable { 1 } else { 0 })?;
for entity_handle in group.iter() {
self.writer.write_handle(340, *entity_handle)?;
}
Ok(())
}
fn write_mlinestyle(&mut self, style: &MLineStyle) -> Result<()> {
self.writer.write_string(0, "MLINESTYLE")?;
self.writer.write_handle(5, style.handle)?;
self.writer.write_handle(330, style.owner)?;
self.writer.write_subclass("AcDbMlineStyle")?;
self.writer.write_string(2, &style.name)?;
self.writer.write_i16(70, style.flags.to_bits() as i16)?;
self.writer.write_string(3, &style.description)?;
let fill_color_index = match style.fill_color {
Color::ByLayer => 256,
Color::ByBlock => 0,
Color::Index(i) => i as i16,
Color::Rgb { .. } => 256, };
self.writer.write_i16(62, fill_color_index)?;
self.writer.write_double(51, style.start_angle.to_degrees())?;
self.writer.write_double(52, style.end_angle.to_degrees())?;
self.writer.write_i16(71, style.element_count() as i16)?;
for element in style.iter() {
self.writer.write_double(49, element.offset)?;
let elem_color_index = match element.color {
Color::ByLayer => 256,
Color::ByBlock => 0,
Color::Index(i) => i as i16,
Color::Rgb { .. } => 256,
};
self.writer.write_i16(62, elem_color_index)?;
self.writer.write_string(6, &element.linetype)?;
}
Ok(())
}
fn write_image_definition(&mut self, imagedef: &ImageDefinition) -> Result<()> {
self.writer.write_string(0, "IMAGEDEF")?;
self.writer.write_handle(5, imagedef.handle)?;
self.writer.write_handle(330, imagedef.owner)?;
self.writer.write_subclass("AcDbRasterImageDef")?;
self.writer.write_i32(90, imagedef.class_version)?;
self.writer.write_string(1, &imagedef.file_name)?;
self.writer
.write_double(10, imagedef.size_in_pixels.0 as f64)?;
self.writer
.write_double(20, imagedef.size_in_pixels.1 as f64)?;
self.writer.write_double(11, imagedef.pixel_size.0)?;
self.writer.write_double(21, imagedef.pixel_size.1)?;
self.writer
.write_byte(280, if imagedef.is_loaded { 1 } else { 0 })?;
self.writer
.write_byte(281, imagedef.resolution_unit.to_code() as u8)?;
Ok(())
}
fn write_plot_settings(&mut self, settings: &PlotSettings) -> Result<()> {
self.writer.write_string(0, "PLOTSETTINGS")?;
self.writer.write_handle(5, settings.handle)?;
self.writer.write_handle(330, settings.owner)?;
self.writer.write_subclass("AcDbPlotSettings")?;
self.writer.write_string(1, &settings.page_name)?;
self.writer.write_string(2, &settings.printer_name)?;
self.writer.write_string(4, &settings.paper_size)?;
self.writer.write_string(6, &settings.plot_view_name)?;
self.writer.write_string(7, &settings.current_style_sheet)?;
self.writer.write_double(40, settings.margins.left)?;
self.writer.write_double(41, settings.margins.bottom)?;
self.writer.write_double(42, settings.margins.right)?;
self.writer.write_double(43, settings.margins.top)?;
self.writer.write_double(44, settings.paper_width)?;
self.writer.write_double(45, settings.paper_height)?;
self.writer.write_double(46, settings.origin_x)?;
self.writer.write_double(47, settings.origin_y)?;
self.writer
.write_double(48, settings.plot_window.lower_left_x)?;
self.writer
.write_double(49, settings.plot_window.lower_left_y)?;
self.writer
.write_double(140, settings.plot_window.upper_right_x)?;
self.writer
.write_double(141, settings.plot_window.upper_right_y)?;
self.writer.write_double(142, settings.scale_numerator)?;
self.writer.write_double(143, settings.scale_denominator)?;
self.writer.write_i16(70, settings.flags.to_bits() as i16)?;
self.writer.write_i16(72, settings.paper_units.to_code())?;
self.writer.write_i16(73, settings.rotation.to_code())?;
self.writer.write_i16(74, settings.plot_type.to_code())?;
self.writer.write_i16(75, settings.scale_type.to_code())?;
self.writer
.write_i16(76, settings.shade_plot_mode.to_code())?;
self.writer
.write_i16(77, settings.shade_plot_resolution.to_code())?;
self.writer.write_i16(78, settings.shade_plot_dpi)?;
Ok(())
}
fn write_multileader_style(&mut self, style: &MultiLeaderStyle) -> Result<()> {
self.writer.write_string(0, "MLEADERSTYLE")?;
self.writer.write_handle(5, style.handle)?;
self.writer.write_handle(330, style.owner_handle)?;
self.writer.write_subclass("AcDbMLeaderStyle")?;
self.writer.write_i16(170, style.content_type as i16)?;
self.writer.write_i16(171, style.multileader_draw_order as i16)?;
self.writer.write_i16(172, style.leader_draw_order as i16)?;
self.writer.write_i32(90, style.max_leader_points)?;
self.writer.write_double(40, style.first_segment_angle)?;
self.writer.write_double(41, style.second_segment_angle)?;
self.writer.write_i16(173, style.path_type as i16)?;
self.write_color_i32(91, style.line_color)?;
{
let h = style.line_type_handle
.filter(|h| *h != Handle::NULL)
.unwrap_or(self.bylayer_linetype_handle);
if h != Handle::NULL {
self.writer.write_handle(340, h)?;
}
}
self.writer.write_i32(92, style.line_weight.value() as i32)?;
self.writer.write_bool(290, style.enable_landing)?;
self.writer.write_double(42, style.landing_gap)?;
self.writer.write_bool(291, style.enable_dogleg)?;
self.writer.write_double(43, style.landing_distance)?;
self.writer.write_string(3, &style.name)?;
if let Some(h) = style.arrowhead_handle {
self.writer.write_handle(341, h)?;
}
self.writer.write_double(44, style.arrowhead_size)?;
self.writer.write_string(300, &style.default_text)?;
if let Some(h) = style.text_style_handle {
self.writer.write_handle(342, h)?;
}
self.writer.write_i16(174, style.text_left_attachment as i16)?;
self.writer.write_i16(175, style.text_angle_type as i16)?;
self.writer.write_i16(176, style.text_alignment as i16)?;
self.writer.write_i16(178, style.text_right_attachment as i16)?;
self.write_color_i32(93, style.text_color)?;
self.writer.write_double(45, style.text_height)?;
self.writer.write_bool(292, style.text_frame)?;
self.writer.write_bool(297, style.text_always_left)?;
self.writer.write_double(46, style.align_space)?;
if let Some(h) = style.block_content_handle {
self.writer.write_handle(343, h)?;
}
self.write_color_i32(94, style.block_content_color)?;
self.writer.write_double(47, style.block_content_scale_x)?;
self.writer.write_double(49, style.block_content_scale_y)?;
self.writer.write_double(140, style.block_content_scale_z)?;
self.writer.write_bool(293, style.enable_block_scale)?;
self.writer.write_double(141, style.block_content_rotation)?;
self.writer.write_bool(294, style.enable_block_rotation)?;
self.writer.write_i16(177, style.block_content_connection as i16)?;
self.writer.write_double(142, style.scale_factor)?;
self.writer.write_bool(295, style.property_changed)?;
self.writer.write_bool(296, style.is_annotative)?;
self.writer.write_double(143, style.break_gap_size)?;
Ok(())
}
fn write_table_style(&mut self, style: &TableStyle) -> Result<()> {
self.writer.write_string(0, "TABLESTYLE")?;
self.writer.write_handle(5, style.handle)?;
self.writer.write_handle(330, style.owner_handle)?;
self.writer.write_subclass("AcDbTableStyle")?;
self.writer.write_byte(280, style.version as u8)?;
if !style.description.is_empty() {
self.writer.write_string(3, &style.description)?;
}
self.writer.write_i16(70, style.flow_direction as i16)?;
self.writer.write_i16(71, style.flags.bits())?;
self.writer.write_double(40, style.horizontal_margin)?;
self.writer.write_double(41, style.vertical_margin)?;
self.writer.write_byte(280, style.title_suppressed as u8)?;
self.writer.write_byte(281, style.header_suppressed as u8)?;
self.write_table_cell_style("DATA", &style.data_row_style)?;
self.write_table_cell_style("HEADER", &style.header_row_style)?;
self.write_table_cell_style("TITLE", &style.title_row_style)?;
Ok(())
}
fn write_table_cell_style(&mut self, name: &str, style: &crate::objects::RowCellStyle) -> Result<()> {
self.writer.write_string(7, &style.text_style_name)?;
self.writer.write_double(140, style.text_height)?;
self.writer.write_i16(170, style.alignment as i16)?;
self.write_color_i16(62, style.text_color)?;
self.write_color_i16(63, style.fill_color)?;
self.writer.write_byte(283, style.fill_enabled as u8)?;
let _ = name;
Ok(())
}
fn write_scale(&mut self, scale: &Scale) -> Result<()> {
self.writer.write_string(0, "SCALE")?;
self.writer.write_handle(5, scale.handle)?;
self.writer.write_handle(330, scale.owner_handle)?;
self.writer.write_subclass("AcDbScale")?;
self.writer.write_string(300, &scale.name)?;
self.writer.write_double(140, scale.paper_units)?;
self.writer.write_double(141, scale.drawing_units)?;
self.writer.write_bool(290, scale.is_unit_scale)?;
Ok(())
}
fn write_sort_entities_table(&mut self, table: &SortEntitiesTable) -> Result<()> {
self.writer.write_string(0, "SORTENTSTABLE")?;
self.writer.write_handle(5, table.handle)?;
self.writer.write_handle(330, table.owner_handle)?;
self.writer.write_subclass("AcDbSortentsTable")?;
self.writer.write_handle(330, table.block_owner_handle)?;
let entries: Vec<_> = table.entries().collect();
let mut sorted_indices: Vec<usize> = (0..entries.len()).collect();
sorted_indices.sort_by_key(|&i| entries[i].sort_handle.value());
let mut new_handles = vec![Handle::NULL; entries.len()];
for &idx in &sorted_indices {
new_handles[idx] = self.allocate_handle();
}
for (i, entry) in entries.iter().enumerate() {
self.writer.write_handle(331, entry.entity_handle)?;
self.writer.write_handle(5, new_handles[i])?;
}
Ok(())
}
fn write_dictionary_variable(&mut self, var: &DictionaryVariable) -> Result<()> {
self.writer.write_string(0, "DICTIONARYVAR")?;
self.writer.write_handle(5, var.handle)?;
if var.owner_handle != Handle::NULL {
self.writer.write_string(102, "{ACAD_REACTORS")?;
self.writer.write_handle(330, var.owner_handle)?;
self.writer.write_string(102, "}")?;
}
self.writer.write_handle(330, var.owner_handle)?;
self.writer.write_subclass("DictionaryVariables")?;
self.writer.write_byte(280, var.schema_number as u8)?;
self.writer.write_string(1, &var.value)?;
Ok(())
}
fn write_visualstyle(&mut self, obj: &VisualStyle) -> Result<()> {
self.writer.write_string(0, "VISUALSTYLE")?;
self.writer.write_handle(5, obj.handle)?;
self.writer.write_handle(330, obj.owner)?;
self.writer.write_subclass("AcDbVisualStyle")?;
self.writer.write_string(2, &obj.description)?;
self.writer.write_i16(70, obj.style_type)?;
self.writer.write_i16(71, obj.face_lighting_model)?;
self.writer.write_i16(72, obj.face_lighting_quality)?;
self.writer.write_i16(73, obj.face_color_mode)?;
self.writer.write_i32(90, obj.face_modifier)?;
self.writer.write_i32(91, obj.edge_model)?;
self.writer.write_i32(92, obj.edge_style)?;
if obj.internal_use_only {
self.writer.write_bool(291, obj.internal_use_only)?;
}
Ok(())
}
fn write_material(&mut self, obj: &Material) -> Result<()> {
self.writer.write_string(0, "MATERIAL")?;
self.writer.write_handle(5, obj.handle)?;
self.writer.write_handle(330, obj.owner)?;
self.writer.write_subclass("AcDbMaterial")?;
self.writer.write_string(1, &obj.name)?;
if !obj.description.is_empty() {
self.writer.write_string(2, &obj.description)?;
}
Ok(())
}
fn write_imagedef_reactor(&mut self, obj: &ImageDefinitionReactor) -> Result<()> {
self.writer.write_string(0, "IMAGEDEF_REACTOR")?;
self.writer.write_handle(5, obj.handle)?;
self.writer.write_handle(330, obj.owner)?;
self.writer.write_subclass("AcDbRasterImageDefReactor")?;
self.writer.write_i32(90, 2)?; self.writer.write_handle(330, obj.image_handle)?;
Ok(())
}
fn write_raster_variables(&mut self, obj: &RasterVariables) -> Result<()> {
self.writer.write_string(0, "RASTERVARIABLES")?;
self.writer.write_handle(5, obj.handle)?;
self.writer.write_handle(330, obj.owner)?;
self.writer.write_subclass("AcDbRasterVariables")?;
self.writer.write_i32(90, obj.class_version)?;
self.writer.write_i16(70, obj.display_image_frame)?;
self.writer.write_i16(71, obj.image_quality)?;
self.writer.write_i16(72, obj.units)?;
Ok(())
}
fn write_bookcolor(&mut self, obj: &BookColor) -> Result<()> {
self.writer.write_string(0, "DBCOLOR")?;
self.writer.write_handle(5, obj.handle)?;
self.writer.write_handle(330, obj.owner)?;
self.writer.write_subclass("AcDbColor")?;
if !obj.color_name.is_empty() {
self.writer.write_string(1, &obj.color_name)?;
}
if !obj.book_name.is_empty() {
self.writer.write_string(2, &obj.book_name)?;
}
Ok(())
}
fn write_dict_with_default(&mut self, obj: &DictionaryWithDefault, objects: &std::collections::HashMap<Handle, ObjectType>) -> Result<()> {
self.writer.write_string(0, "ACDBDICTIONARYWDFLT")?;
self.writer.write_handle(5, obj.handle)?;
self.writer.write_handle(330, obj.owner)?;
self.writer.write_subclass("AcDbDictionary")?;
self.writer.write_i16(281, obj.duplicate_cloning)?;
for (key, handle) in &obj.entries {
if !objects.contains_key(handle) {
continue;
}
self.writer.write_string(3, key)?;
self.writer.write_handle(350, *handle)?;
}
self.writer.write_subclass("AcDbDictionaryWithDefault")?;
self.writer.write_handle(340, obj.default_handle)?;
Ok(())
}
fn write_wipeout_variables(&mut self, obj: &WipeoutVariables) -> Result<()> {
self.writer.write_string(0, "WIPEOUTVARIABLES")?;
self.writer.write_handle(5, obj.handle)?;
self.writer.write_handle(330, obj.owner)?;
self.writer.write_subclass("AcDbWipeoutVariables")?;
self.writer.write_i16(70, obj.display_frame)?;
Ok(())
}
fn write_stub_handle_only(&mut self, type_name: &str, handle: Handle, owner: Handle) -> Result<()> {
self.writer.write_string(0, type_name)?;
self.writer.write_handle(5, handle)?;
self.writer.write_handle(330, owner)?;
Ok(())
}
fn write_unknown_object(
&mut self,
type_name: &str,
handle: Handle,
owner: Handle,
raw_dxf_codes: Option<&[(i32, String)]>,
) -> Result<()> {
if let Some(codes) = raw_dxf_codes {
self.writer.write_string(0, type_name)?;
self.writer.write_handle(5, handle)?;
self.writer.write_handle(330, owner)?;
for (code, value) in codes {
self.writer.write_string(*code, value)?;
}
}
Ok(())
}
fn write_color_i32(&mut self, code: i32, color: Color) -> Result<()> {
match color {
Color::ByLayer => self.writer.write_i32(code, 256)?,
Color::ByBlock => self.writer.write_i32(code, 0)?,
Color::Index(i) => self.writer.write_i32(code, i as i32)?,
Color::Rgb { r, g, b } => {
let rgb = ((r as i32) << 16) | ((g as i32) << 8) | (b as i32);
self.writer.write_i32(code, rgb)?;
}
}
Ok(())
}
fn write_color_i16(&mut self, code: i32, color: Color) -> Result<()> {
match color {
Color::ByLayer => self.writer.write_i16(code, 256)?,
Color::ByBlock => self.writer.write_i16(code, 0)?,
Color::Index(i) => self.writer.write_i16(code, i as i16)?,
Color::Rgb { .. } => self.writer.write_i16(code, 7)?, }
Ok(())
}
#[allow(dead_code)]
fn write_xdata(&mut self, xdata: &ExtendedData) -> Result<()> {
if xdata.is_empty() {
return Ok(());
}
for record in xdata.records() {
self.writer.write_string(1001, &record.application_name)?;
for value in &record.values {
match value {
XDataValue::String(s) => {
self.writer.write_string(1000, s)?;
}
XDataValue::ControlString(s) => {
self.writer.write_string(1002, s)?;
}
XDataValue::LayerName(s) => {
self.writer.write_string(1003, s)?;
}
XDataValue::BinaryData(data) => {
self.writer.write_binary(1004, data)?;
}
XDataValue::Handle(h) => {
self.writer.write_handle(1005, *h)?;
}
XDataValue::Point3D(p) => {
self.writer.write_double(1010, p.x)?;
self.writer.write_double(1020, p.y)?;
self.writer.write_double(1030, p.z)?;
}
XDataValue::Position3D(p) => {
self.writer.write_double(1011, p.x)?;
self.writer.write_double(1021, p.y)?;
self.writer.write_double(1031, p.z)?;
}
XDataValue::Displacement3D(p) => {
self.writer.write_double(1012, p.x)?;
self.writer.write_double(1022, p.y)?;
self.writer.write_double(1032, p.z)?;
}
XDataValue::Direction3D(p) => {
self.writer.write_double(1013, p.x)?;
self.writer.write_double(1023, p.y)?;
self.writer.write_double(1033, p.z)?;
}
XDataValue::Real(r) => {
self.writer.write_double(1040, *r)?;
}
XDataValue::Distance(d) => {
self.writer.write_double(1041, *d)?;
}
XDataValue::ScaleFactor(s) => {
self.writer.write_double(1042, *s)?;
}
XDataValue::Integer16(i) => {
self.writer.write_i16(1070, *i)?;
}
XDataValue::Integer32(i) => {
self.writer.write_i32(1071, *i)?;
}
}
}
}
Ok(())
}
fn write_multileader(&mut self, mleader: &crate::entities::MultiLeader, owner: Handle) -> Result<()> {
self.writer.write_entity_type("MULTILEADER")?;
self.write_common_entity_data(&mleader.common, owner)?;
self.writer.write_subclass("AcDbMLeader")?;
self.writer.write_i16(270, 2)?;
let ctx = &mleader.context;
self.writer.write_string(300, "CONTEXT_DATA{")?;
self.writer.write_double(40, ctx.scale_factor)?;
self.writer.write_point3d(10, ctx.content_base_point)?;
self.writer.write_double(41, ctx.text_height)?;
self.writer.write_double(140, ctx.arrowhead_size)?;
self.writer.write_double(145, ctx.landing_gap)?;
self.writer.write_i16(174, ctx.text_left_attachment as i16)?;
self.writer.write_i16(175, ctx.text_right_attachment as i16)?;
self.writer.write_i16(176, ctx.block_connection_type as i16)?;
self.writer.write_i16(177, 0)?;
self.writer.write_bool(290, ctx.has_text_contents)?;
if ctx.has_text_contents {
self.writer.write_string(304, &ctx.text_string)?;
self.writer.write_point3d(11, ctx.text_normal)?;
if let Some(h) = ctx.text_style_handle {
self.writer.write_handle(340, h)?;
}
self.writer.write_point3d(12, ctx.text_location)?;
self.writer.write_point3d(13, ctx.text_direction)?;
self.writer.write_double(42, ctx.text_rotation)?;
self.writer.write_double(43, ctx.text_width)?;
self.writer.write_double(44, ctx.text_boundary_height)?;
self.writer.write_double(45, ctx.line_spacing_factor)?;
self.writer.write_i16(170, ctx.line_spacing_style as i16)?;
self.write_color_i32(90, ctx.text_color)?;
self.writer.write_i16(171, ctx.text_alignment as i16)?;
self.writer.write_i16(172, ctx.text_flow_direction as i16)?;
self.write_color_i32(91, ctx.background_fill_color)?;
self.writer.write_double(141, ctx.column_width)?;
self.write_color_i32(92, ctx.background_fill_color)?;
self.writer.write_bool(291, ctx.background_fill_enabled)?;
self.writer.write_bool(292, ctx.word_break)?;
self.writer.write_i16(173, ctx.column_type)?;
self.writer.write_bool(293, ctx.column_flow_reversed)?;
self.writer.write_double(142, ctx.column_width)?;
self.writer.write_double(143, ctx.column_gutter)?;
self.writer.write_bool(294, ctx.text_height_automatic)?;
}
self.writer.write_bool(295, ctx.background_mask_fill_on)?;
self.writer.write_bool(296, ctx.has_block_contents)?;
if ctx.has_block_contents {
if let Some(h) = ctx.block_content_handle {
self.writer.write_handle(341, h)?;
}
self.writer.write_point3d(14, ctx.block_content_normal)?;
self.writer.write_point3d(15, ctx.block_content_location)?;
self.writer.write_point3d(16, ctx.block_content_scale)?;
self.writer.write_double(46, ctx.block_rotation)?;
self.write_color_i32(93, ctx.block_content_color)?;
}
self.writer.write_point3d(110, ctx.base_point)?;
self.writer.write_point3d(111, ctx.base_direction)?;
self.writer.write_point3d(112, ctx.base_vertical)?;
self.writer.write_bool(297, ctx.normal_reversed)?;
for root in &ctx.leader_roots {
self.writer.write_string(302, "LEADER{")?;
self.writer.write_bool(290, root.content_valid)?;
self.writer.write_bool(291, root.unknown)?;
self.writer.write_point3d(10, root.connection_point)?;
self.writer.write_point3d(11, root.direction)?;
self.writer.write_i32(90, root.break_points.len() as i32)?;
for bp in &root.break_points {
self.writer.write_point3d(12, bp.start_point)?;
self.writer.write_point3d(13, bp.end_point)?;
}
self.writer.write_double(40, root.landing_distance)?;
for line in &root.lines {
self.writer.write_string(304, "LEADER_LINE{")?;
for pt in &line.points {
self.writer.write_point3d(10, *pt)?;
}
self.writer.write_i32(91, line.index)?;
self.writer.write_i16(170, line.path_type as i16)?;
self.write_color_i32(92, line.line_color)?;
self.writer.write_handle(340, line.line_type_handle.unwrap_or(Handle::NULL))?;
self.writer.write_i16(171, line.line_weight.value())?;
self.writer.write_double(40, line.arrowhead_size)?;
self.writer.write_handle(341, line.arrowhead_handle.unwrap_or(Handle::NULL))?;
self.writer.write_i32(93, line.override_flags.bits() as i32)?;
self.writer.write_i16(271, line.break_info_count as i16)?;
self.writer.write_string(305, "}")?;
}
self.writer.write_string(303, "}")?;
}
self.writer.write_i16(273, ctx.text_top_attachment as i16)?;
self.writer.write_i16(272, ctx.text_bottom_attachment as i16)?;
self.writer.write_string(301, "}")?;
if let Some(h) = mleader.style_handle {
self.writer.write_handle(340, h)?;
}
self.writer.write_i32(90, mleader.property_override_flags.bits() as i32)?;
self.writer.write_i16(170, mleader.content_type as i16)?;
self.write_color_i32(91, mleader.line_color)?;
{
let h = mleader.line_type_handle
.filter(|h| *h != Handle::NULL && *h != self.byblock_linetype_handle)
.unwrap_or(self.bylayer_linetype_handle);
if h != Handle::NULL {
self.writer.write_handle(341, h)?;
}
}
self.writer.write_i16(171, mleader.line_weight.value())?;
self.writer.write_bool(290, mleader.enable_landing)?;
self.writer.write_bool(291, mleader.enable_dogleg)?;
self.writer.write_double(41, mleader.dogleg_length)?;
if let Some(h) = mleader.text_style_handle {
self.writer.write_handle(342, h)?;
}
self.writer.write_double(42, mleader.arrowhead_size)?;
self.writer.write_i16(172, mleader.text_left_attachment as i16)?;
if let Some(h) = mleader.block_content_handle {
self.writer.write_handle(343, h)?;
}
self.writer.write_i16(173, mleader.text_right_attachment as i16)?;
self.writer.write_i32(95, mleader.text_right_attachment as i32)?;
self.writer.write_i16(174, mleader.text_angle_type as i16)?;
self.writer.write_i16(175, mleader.text_alignment as i16)?;
self.write_color_i32(92, mleader.text_color)?;
self.writer.write_bool(292, mleader.text_frame)?;
self.write_color_i32(93, mleader.block_content_color)?;
self.writer.write_point3d(10, mleader.block_scale)?;
self.writer.write_double(43, mleader.block_rotation)?;
self.writer.write_i16(176, mleader.block_connection_type as i16)?;
self.writer.write_bool(293, mleader.enable_annotation_scale)?;
self.writer.write_i16(271, mleader.text_align_in_ipe)?;
self.writer.write_i16(273, mleader.text_bottom_attachment as i16)?;
self.writer.write_i16(272, mleader.text_top_attachment as i16)?;
self.writer.write_bool(295, mleader.extend_leader_to_text)?;
Ok(())
}
fn write_mline(&mut self, mline: &crate::entities::MLine, owner: Handle) -> Result<()> {
self.writer.write_entity_type("MLINE")?;
self.write_common_entity_data(&mline.common, owner)?;
self.writer.write_subclass("AcDbMline")?;
self.writer.write_string(2, &mline.style_name)?;
let style_h = mline.style_handle.unwrap_or(Handle::NULL);
self.writer.write_handle(340, style_h)?;
self.writer.write_double(40, mline.scale_factor)?;
self.writer.write_i16(70, mline.justification as i16)?;
self.writer.write_i16(71, mline.flags.bits())?;
self.writer.write_i16(72, mline.vertices.len() as i16)?;
self.writer.write_i16(73, mline.style_element_count as i16)?;
self.writer.write_point3d(10, mline.start_point)?;
self.writer.write_point3d(210, mline.normal)?;
for vertex in &mline.vertices {
self.writer.write_point3d(11, vertex.position)?;
self.writer.write_point3d(12, vertex.direction)?;
self.writer.write_point3d(13, vertex.miter)?;
for segment in &vertex.segments {
self.writer.write_i16(74, segment.parameters.len() as i16)?;
for param in &segment.parameters {
self.writer.write_double(41, *param)?;
}
self.writer.write_i16(75, segment.area_fill_parameters.len() as i16)?;
for param in &segment.area_fill_parameters {
self.writer.write_double(42, *param)?;
}
}
}
Ok(())
}
fn write_mesh(&mut self, mesh: &crate::entities::Mesh, owner: Handle) -> Result<()> {
self.writer.write_entity_type("MESH")?;
self.write_common_entity_data(&mesh.common, owner)?;
self.writer.write_subclass("AcDbSubDMesh")?;
self.writer.write_i16(71, mesh.version)?;
self.writer.write_i16(72, if mesh.blend_crease { 1 } else { 0 })?;
self.writer.write_i32(91, mesh.subdivision_level)?;
self.writer.write_i32(92, mesh.vertices.len() as i32)?;
for v in &mesh.vertices {
self.writer.write_point3d(10, *v)?;
}
let face_list_size: i32 = mesh.faces.iter().map(|f| 1 + f.vertices.len() as i32).sum();
self.writer.write_i32(93, face_list_size)?;
for face in &mesh.faces {
self.writer.write_i32(90, face.vertices.len() as i32)?;
for vi in &face.vertices {
self.writer.write_i32(90, *vi as i32)?;
}
}
self.writer.write_i32(94, (mesh.edges.len() * 2) as i32)?;
for edge in &mesh.edges {
self.writer.write_i32(90, edge.start as i32)?;
self.writer.write_i32(90, edge.end as i32)?;
}
let creased_edges: Vec<_> = mesh.edges.iter().enumerate()
.filter(|(_, e)| e.has_crease())
.collect();
self.writer.write_i32(95, creased_edges.len() as i32)?;
for (idx, edge) in creased_edges {
self.writer.write_i32(90, idx as i32)?;
self.writer.write_double(140, edge.crease_value())?;
}
Ok(())
}
fn write_raster_image(&mut self, image: &crate::entities::RasterImage, owner: Handle) -> Result<()> {
self.writer.write_entity_type("IMAGE")?;
self.write_common_entity_data(&image.common, owner)?;
self.writer.write_subclass("AcDbRasterImage")?;
self.writer.write_i32(90, image.class_version)?;
self.writer.write_point3d(10, image.insertion_point)?;
self.writer.write_point3d(11, image.u_vector)?;
self.writer.write_point3d(12, image.v_vector)?;
self.writer.write_double(13, image.size.x)?;
self.writer.write_double(23, image.size.y)?;
if let Some(h) = image.definition_handle {
self.writer.write_handle(340, h)?;
}
self.writer.write_i16(70, image.flags.bits())?;
self.writer.write_byte(280, if image.clipping_enabled { 1 } else { 0 })?;
self.writer.write_byte(281, image.brightness)?;
self.writer.write_byte(282, image.contrast)?;
self.writer.write_byte(283, image.fade)?;
if let Some(h) = image.definition_reactor_handle {
self.writer.write_handle(360, h)?;
}
self.writer.write_i16(71, image.clip_boundary.clip_type as i16)?;
self.writer.write_i32(91, image.clip_boundary.vertices.len() as i32)?;
for v in &image.clip_boundary.vertices {
self.writer.write_double(14, v.x)?;
self.writer.write_double(24, v.y)?;
}
Ok(())
}
fn write_solid3d(&mut self, solid: &Solid3D, owner: Handle) -> Result<()> {
self.writer.write_entity_type("3DSOLID")?;
self.write_common_entity_data(&solid.common, owner)?;
self.writer.write_subclass("AcDbModelerGeometry")?;
if self.needs_sab() {
self.writer.write_bool(290, false)?;
self.writer
.write_string(2, "{00000000-0000-0000-0000-000000000000}")?;
self.queue_sab_data(&solid.acis_data, solid.common.handle);
} else {
self.writer.write_i16(70, solid.acis_data.version as i16)?;
self.write_acis_data(&solid.acis_data)?;
}
self.writer.write_subclass("AcDb3dSolid")?;
let h = solid.history_handle.unwrap_or(Handle::NULL);
self.writer.write_handle(350, h)?;
Ok(())
}
fn write_region(&mut self, region: &Region, owner: Handle) -> Result<()> {
self.writer.write_entity_type("REGION")?;
self.write_common_entity_data(®ion.common, owner)?;
self.writer.write_subclass("AcDbModelerGeometry")?;
if self.needs_sab() {
self.writer.write_bool(290, false)?;
self.writer
.write_string(2, "{00000000-0000-0000-0000-000000000000}")?;
self.queue_sab_data(®ion.acis_data, region.common.handle);
} else {
self.writer
.write_i16(70, region.acis_data.version as i16)?;
self.write_acis_data(®ion.acis_data)?;
}
Ok(())
}
fn write_body(&mut self, body: &Body, owner: Handle) -> Result<()> {
self.writer.write_entity_type("BODY")?;
self.write_common_entity_data(&body.common, owner)?;
self.writer.write_subclass("AcDbModelerGeometry")?;
if self.needs_sab() {
self.writer.write_bool(290, false)?;
self.writer
.write_string(2, "{00000000-0000-0000-0000-000000000000}")?;
self.queue_sab_data(&body.acis_data, body.common.handle);
} else {
self.writer.write_i16(70, body.acis_data.version as i16)?;
self.write_acis_data(&body.acis_data)?;
}
Ok(())
}
fn write_acis_data(&mut self, acis: &AcisData) -> Result<()> {
let data = &acis.sat_data;
if data.is_empty() {
self.writer.write_string(1, "")?;
return Ok(());
}
let mut full = AcisData::strip_sat_terminator(data);
full.push_str("End-of-ACIS-data\n");
let encoded = match acis.version {
AcisVersion::Version1 => AcisData::encode_sat(&full),
_ => full,
};
let mut any_written = false;
for line in encoded.lines() {
if line.len() <= 255 {
self.writer.write_string(1, line)?;
} else {
let mut remaining = line;
let mut first = true;
while !remaining.is_empty() {
let end = remaining.len().min(255);
let (chunk, rest) = remaining.split_at(end);
if first {
self.writer.write_string(1, chunk)?;
first = false;
} else {
self.writer.write_string(3, chunk)?;
}
remaining = rest;
}
}
any_written = true;
}
if !any_written {
self.writer.write_string(1, "")?;
}
Ok(())
}
fn write_acad_table(&mut self, table: &table::Table, owner: Handle) -> Result<()> {
self.writer.write_entity_type("ACAD_TABLE")?;
self.write_common_entity_data(&table.common, owner)?;
self.writer.write_subclass("AcDbBlockReference")?;
if let Some(h) = table.block_record_handle {
self.writer.write_handle(2, h)?;
}
self.writer.write_point3d(10, table.insertion_point)?;
self.writer.write_subclass("AcDbTable")?;
if let Some(h) = table.table_style_handle {
self.writer.write_handle(342, h)?;
}
self.writer.write_byte(280, table.data_version as u8)?;
self.writer.write_point3d(11, table.horizontal_direction)?;
self.writer.write_i32(91, table.rows.len() as i32)?;
self.writer.write_i32(92, table.columns.len() as i32)?;
let mut override_flags = 0i32;
if table.override_flag { override_flags |= 1; }
if table.override_border_color { override_flags |= 2; }
if table.override_border_line_weight { override_flags |= 4; }
if table.override_border_visibility { override_flags |= 8; }
self.writer.write_i32(93, override_flags)?;
for row in &table.rows {
self.writer.write_double(141, row.height)?;
}
for col in &table.columns {
self.writer.write_double(142, col.width)?;
}
for row in &table.rows {
for cell in &row.cells {
self.write_table_cell(cell)?;
}
}
self.writer.write_i32(94, table.break_options.bits() as i32)?;
self.writer.write_i32(95, table.break_flow_direction as i32)?;
self.writer.write_double(143, table.break_spacing)?;
Ok(())
}
fn write_table_cell(&mut self, cell: &TableCell) -> Result<()> {
self.writer.write_i16(171, cell.cell_type as i16)?;
self.writer.write_i16(172, cell.state.bits() as i16)?;
self.writer.write_i16(173, cell.flag as i16)?;
self.writer.write_i16(174, cell.merged as i16)?;
self.writer.write_i16(175, cell.merge_width as i16)?;
self.writer.write_i16(176, cell.merge_height as i16)?;
self.writer.write_i16(177, cell.virtual_edge)?;
self.writer.write_double(144, cell.rotation)?;
self.writer.write_i16(179, cell.contents.len() as i16)?;
for content in &cell.contents {
self.writer.write_i16(170, content.content_type as i16)?;
match content.value.value_type {
CellValueType::String => {
self.writer.write_string(1, &content.value.text)?;
}
CellValueType::Double => {
self.writer.write_double(140, content.value.numeric_value)?;
}
CellValueType::Long => {
self.writer.write_i32(90, content.value.numeric_value as i32)?;
}
_ => {}
}
if !content.value.format.is_empty() {
self.writer.write_string(300, &content.value.format)?;
}
if let Some(h) = content.block_handle {
self.writer.write_handle(340, h)?;
}
}
if let Some(ref style) = cell.style {
self.writer.write_color(62, style.content_color)?;
self.writer.write_double(140, style.text_height)?;
self.writer.write_double(144, style.rotation)?;
self.writer.write_i16(170, style.alignment as i16)?;
}
Ok(())
}
fn write_tolerance(&mut self, tolerance: &Tolerance, owner: Handle) -> Result<()> {
self.writer.write_entity_type("TOLERANCE")?;
self.write_common_entity_data(&tolerance.common, owner)?;
self.writer.write_subclass("AcDbFcf")?;
self.writer.write_string(3, &tolerance.dimension_style_name)?;
self.writer.write_double(10, tolerance.insertion_point.x)?;
self.writer.write_double(20, tolerance.insertion_point.y)?;
self.writer.write_double(30, tolerance.insertion_point.z)?;
self.writer.write_double(210, tolerance.normal.x)?;
self.writer.write_double(220, tolerance.normal.y)?;
self.writer.write_double(230, tolerance.normal.z)?;
self.writer.write_double(11, tolerance.direction.x)?;
self.writer.write_double(21, tolerance.direction.y)?;
self.writer.write_double(31, tolerance.direction.z)?;
self.writer.write_string(1, &tolerance.text)?;
Ok(())
}
fn write_polyface_mesh(&mut self, mesh: &PolyfaceMesh, owner: Handle) -> Result<()> {
self.writer.write_entity_type("POLYLINE")?;
self.write_common_entity_data(&mesh.common, owner)?;
self.writer.write_subclass("AcDbPolyFaceMesh")?;
self.writer.write_i16(66, 1)?;
self.writer.write_double(10, 0.0)?;
self.writer.write_double(20, 0.0)?;
self.writer.write_double(30, mesh.elevation)?;
self.writer.write_i16(70, mesh.flags.bits())?;
self.writer.write_i16(71, mesh.vertex_count() as i16)?;
self.writer.write_i16(72, mesh.face_count() as i16)?;
for vertex in mesh.vertices.iter() {
let vertex_handle = if vertex.common.handle.is_null() {
self.allocate_handle()
} else {
vertex.common.handle
};
self.writer.write_entity_type("VERTEX")?;
self.writer.write_handle(5, vertex_handle)?;
self.writer.write_handle(330, mesh.common.handle)?;
self.writer.write_string(8, &vertex.common.layer)?;
self.writer.write_double(10, vertex.location.x)?;
self.writer.write_double(20, vertex.location.y)?;
self.writer.write_double(30, vertex.location.z)?;
let flags = vertex.flags | PolyfaceVertexFlags::POLYGON_MESH;
self.writer.write_i16(70, flags.bits())?;
}
for face in mesh.faces.iter() {
let face_handle = if face.common.handle.is_null() {
self.allocate_handle()
} else {
face.common.handle
};
self.writer.write_entity_type("VERTEX")?;
self.writer.write_handle(5, face_handle)?;
self.writer.write_handle(330, mesh.common.handle)?;
self.writer.write_string(8, &face.common.layer)?;
self.writer.write_double(10, 0.0)?;
self.writer.write_double(20, 0.0)?;
self.writer.write_double(30, 0.0)?;
let flags = face.flags | PolyfaceVertexFlags::POLYFACE_MESH;
self.writer.write_i16(70, flags.bits())?;
let indices = face.vertex_indices();
if indices.len() >= 3 {
self.writer.write_i16(71, indices[0])?;
self.writer.write_i16(72, indices[1])?;
self.writer.write_i16(73, indices[2])?;
if indices.len() >= 4 {
self.writer.write_i16(74, indices[3])?;
}
}
}
self.writer.write_entity_type("SEQEND")?;
let seqend_handle = mesh.seqend_handle.unwrap_or_else(|| self.allocate_handle());
self.writer.write_handle(5, seqend_handle)?;
self.writer.write_handle(330, mesh.common.handle)?;
self.writer.write_string(8, &mesh.common.layer)?;
Ok(())
}
fn write_wipeout(&mut self, wipeout: &Wipeout, owner: Handle) -> Result<()> {
self.writer.write_entity_type("WIPEOUT")?;
self.write_common_entity_data(&wipeout.common, owner)?;
self.writer.write_subclass("AcDbWipeout")?;
self.writer.write_i32(90, wipeout.class_version)?;
self.writer.write_double(10, wipeout.insertion_point.x)?;
self.writer.write_double(20, wipeout.insertion_point.y)?;
self.writer.write_double(30, wipeout.insertion_point.z)?;
self.writer.write_double(11, wipeout.u_vector.x)?;
self.writer.write_double(21, wipeout.u_vector.y)?;
self.writer.write_double(31, wipeout.u_vector.z)?;
self.writer.write_double(12, wipeout.v_vector.x)?;
self.writer.write_double(22, wipeout.v_vector.y)?;
self.writer.write_double(32, wipeout.v_vector.z)?;
self.writer.write_double(13, wipeout.size.x)?;
self.writer.write_double(23, wipeout.size.y)?;
self.writer.write_i16(70, wipeout.flags.bits())?;
self.writer.write_byte(280, if wipeout.clipping_enabled { 1 } else { 0 })?;
self.writer.write_byte(281, wipeout.brightness)?;
self.writer.write_byte(282, wipeout.contrast)?;
self.writer.write_byte(283, wipeout.fade)?;
self.writer.write_i16(71, wipeout.clip_type as i16)?;
self.writer.write_i32(91, wipeout.clip_boundary_vertices.len() as i32)?;
for v in &wipeout.clip_boundary_vertices {
self.writer.write_double(14, v.x)?;
self.writer.write_double(24, v.y)?;
}
Ok(())
}
fn write_shape(&mut self, shape: &Shape, owner: Handle) -> Result<()> {
self.writer.write_entity_type("SHAPE")?;
self.write_common_entity_data(&shape.common, owner)?;
self.writer.write_subclass("AcDbShape")?;
if shape.thickness.abs() > 1e-10 {
self.writer.write_double(39, shape.thickness)?;
}
self.writer.write_double(10, shape.insertion_point.x)?;
self.writer.write_double(20, shape.insertion_point.y)?;
self.writer.write_double(30, shape.insertion_point.z)?;
self.writer.write_double(40, shape.size)?;
self.writer.write_string(2, &shape.shape_name)?;
if shape.rotation.abs() > 1e-10 {
self.writer.write_double(50, shape.rotation.to_degrees())?;
}
if (shape.relative_x_scale - 1.0).abs() > 1e-10 {
self.writer.write_double(41, shape.relative_x_scale)?;
}
if shape.oblique_angle.abs() > 1e-10 {
self.writer.write_double(51, shape.oblique_angle.to_degrees())?;
}
if shape.has_custom_normal() {
self.writer.write_double(210, shape.normal.x)?;
self.writer.write_double(220, shape.normal.y)?;
self.writer.write_double(230, shape.normal.z)?;
}
Ok(())
}
fn write_underlay(&mut self, underlay: &Underlay, owner: Handle) -> Result<()> {
self.writer.write_entity_type(underlay.entity_name())?;
self.write_common_entity_data(&underlay.common, owner)?;
self.writer.write_subclass("AcDbUnderlayReference")?;
self.writer.write_handle(340, underlay.definition_handle)?;
self.writer.write_double(10, underlay.insertion_point.x)?;
self.writer.write_double(20, underlay.insertion_point.y)?;
self.writer.write_double(30, underlay.insertion_point.z)?;
self.writer.write_double(41, underlay.x_scale)?;
self.writer.write_double(42, underlay.y_scale)?;
self.writer.write_double(43, underlay.z_scale)?;
self.writer.write_double(50, underlay.rotation.to_degrees())?;
self.writer.write_double(210, underlay.normal.x)?;
self.writer.write_double(220, underlay.normal.y)?;
self.writer.write_double(230, underlay.normal.z)?;
self.writer.write_byte(280, underlay.flags.bits())?;
self.writer.write_byte(281, underlay.contrast)?;
self.writer.write_byte(282, underlay.fade)?;
self.writer.write_i32(91, underlay.clip_boundary_vertices.len() as i32)?;
for v in &underlay.clip_boundary_vertices {
self.writer.write_double(11, v.x)?;
self.writer.write_double(21, v.y)?;
}
Ok(())
}
fn write_seqend(&mut self, seqend: &Seqend, owner: Handle) -> Result<()> {
self.writer.write_entity_type("SEQEND")?;
self.write_common_entity_data(&seqend.common, owner)?;
Ok(())
}
fn write_ole2frame(&mut self, ole: &Ole2Frame, owner: Handle) -> Result<()> {
self.writer.write_entity_type("OLE2FRAME")?;
self.write_common_entity_data(&ole.common, owner)?;
self.writer.write_subclass("AcDbOle2Frame")?;
self.writer.write_i16(70, ole.version)?;
if !ole.source_application.is_empty() {
self.writer.write_string(3, &ole.source_application)?;
}
self.writer.write_double(10, ole.upper_left_corner.x)?;
self.writer.write_double(20, ole.upper_left_corner.y)?;
self.writer.write_double(30, ole.upper_left_corner.z)?;
self.writer.write_double(11, ole.lower_right_corner.x)?;
self.writer.write_double(21, ole.lower_right_corner.y)?;
self.writer.write_double(31, ole.lower_right_corner.z)?;
self.writer.write_i16(71, ole.ole_object_type as i16)?;
self.writer.write_i16(72, if ole.is_paper_space { 1 } else { 0 })?;
self.writer.write_i16(73, 3)?; if !ole.binary_data.is_empty() {
self.writer.write_i32(90, ole.binary_data.len() as i32)?;
for chunk in ole.binary_data.chunks(127) {
let hex: String = chunk.iter().map(|b| format!("{:02X}", b)).collect();
self.writer.write_string(310, &hex)?;
}
}
self.writer.write_string(1, "OLE")?;
Ok(())
}
fn write_polygon_mesh(&mut self, mesh: &PolygonMeshEntity, owner: Handle) -> Result<()> {
use crate::entities::polygon_mesh::PolygonMeshFlags;
self.writer.write_entity_type("POLYLINE")?;
self.write_common_entity_data(&mesh.common, owner)?;
self.writer.write_subclass("AcDbPolygonMesh")?;
self.writer.write_i16(66, 1)?;
self.writer.write_double(10, 0.0)?;
self.writer.write_double(20, 0.0)?;
self.writer.write_double(30, 0.0)?;
let flags = mesh.flags | PolygonMeshFlags::POLYGON_MESH;
self.writer.write_i16(70, flags.bits())?;
self.writer.write_i16(71, mesh.m_vertex_count)?;
self.writer.write_i16(72, mesh.n_vertex_count)?;
self.writer.write_i16(73, mesh.m_smooth_density)?;
self.writer.write_i16(74, mesh.n_smooth_density)?;
self.writer.write_i16(75, mesh.smooth_type as i16)?;
if mesh.normal != Vector3::UNIT_Z {
self.writer.write_double(210, mesh.normal.x)?;
self.writer.write_double(220, mesh.normal.y)?;
self.writer.write_double(230, mesh.normal.z)?;
}
let mesh_handle = mesh.common.handle;
for vertex in &mesh.vertices {
let vertex_handle = if vertex.common.handle.is_null() {
self.allocate_handle()
} else {
vertex.common.handle
};
self.writer.write_entity_type("VERTEX")?;
self.writer.write_handle(5, vertex_handle)?;
self.writer.write_handle(330, mesh_handle)?;
self.writer.write_string(8, &vertex.common.layer)?;
if mesh.common.color != Color::ByLayer {
self.writer.write_color(62, mesh.common.color)?;
}
self.writer.write_point3d(10, vertex.location)?;
if vertex.flags != 0 {
self.writer.write_i16(70, vertex.flags)?;
}
}
let seqend_handle = self.allocate_handle();
self.writer.write_entity_type("SEQEND")?;
self.writer.write_handle(5, seqend_handle)?;
self.writer.write_handle(330, mesh_handle)?;
self.writer.write_string(8, &mesh.common.layer)?;
Ok(())
}
fn queue_sab_data(&mut self, acis: &AcisData, entity_handle: Handle) {
if acis.is_binary && !acis.sab_data.is_empty() {
self.sab_entries
.push((entity_handle, acis.sab_data.clone()));
} else if !acis.sat_data.is_empty() {
if let Ok(mut sat_doc) =
crate::entities::acis::SatDocument::parse(&acis.sat_data)
{
sat_doc.strip_for_sab();
let sab = crate::entities::acis::SabWriter::write(&sat_doc);
self.sab_entries.push((entity_handle, sab));
}
}
}
pub fn write_acdsdata(&mut self) -> Result<()> {
if self.sab_entries.is_empty() {
return Ok(());
}
self.writer.write_section_start("ACDSDATA")?;
self.writer.write_i16(70, 2)?;
self.writer.write_i16(71, 2)?;
self.write_acds_thumbnail_schema()?;
self.write_acds_asm_schema()?;
self.write_acds_infrastructure_schemas()?;
let entries = std::mem::take(&mut self.sab_entries);
for (entity_handle, sab_data) in &entries {
self.write_acds_record(*entity_handle, sab_data)?;
}
self.sab_entries = entries;
self.writer.write_section_end()?;
Ok(())
}
fn write_acds_thumbnail_schema(&mut self) -> Result<()> {
self.writer.write_string(0, "ACDSSCHEMA")?;
self.writer.write_i32(90, 0)?;
self.writer.write_string(1, "AcDb_Thumbnail_Schema")?;
self.writer.write_string(2, "AcDbDs::ID")?;
self.writer.write_byte(280, 10)?;
self.writer.write_i32(91, 8)?;
self.writer.write_string(2, "Thumbnail_Data")?;
self.writer.write_byte(280, 15)?;
self.writer.write_i32(91, 0)?;
self.write_acds_schema_records(0)?;
Ok(())
}
fn write_acds_asm_schema(&mut self) -> Result<()> {
self.writer.write_string(0, "ACDSSCHEMA")?;
self.writer.write_i32(90, 1)?;
self.writer.write_string(1, "AcDb3DSolid_ASM_Data")?;
self.writer.write_string(2, "AcDbDs::ID")?;
self.writer.write_byte(280, 10)?;
self.writer.write_i32(91, 8)?;
self.writer.write_string(2, "ASM_Data")?;
self.writer.write_byte(280, 15)?;
self.writer.write_i32(91, 0)?;
self.write_acds_schema_records(1)?;
Ok(())
}
fn write_acds_schema_records(&mut self, schema_id: i32) -> Result<()> {
self.writer.write_string(101, "ACDSRECORD")?;
self.writer.write_i32(95, schema_id)?;
self.writer.write_i32(90, 2)?;
self.writer.write_string(2, "AcDbDs::TreatedAsObjectData")?;
self.writer.write_byte(280, 1)?;
self.writer.write_bool(291, true)?;
self.writer.write_string(101, "ACDSRECORD")?;
self.writer.write_i32(95, schema_id)?;
self.writer.write_i32(90, 3)?;
self.writer.write_string(2, "AcDbDs::Legacy")?;
self.writer.write_byte(280, 1)?;
self.writer.write_bool(291, true)?;
self.writer.write_string(101, "ACDSRECORD")?;
self.writer.write_string(1, "AcDbDs::ID")?;
self.writer.write_i32(90, 4)?;
self.writer.write_string(2, "AcDs:Indexable")?;
self.writer.write_byte(280, 1)?;
self.writer.write_bool(291, true)?;
self.writer.write_string(101, "ACDSRECORD")?;
self.writer.write_string(1, "AcDbDs::ID")?;
self.writer.write_i32(90, 5)?;
self.writer.write_string(2, "AcDbDs::HandleAttribute")?;
self.writer.write_byte(280, 7)?;
self.writer.write_i16(282, 1)?;
Ok(())
}
fn write_acds_infrastructure_schemas(&mut self) -> Result<()> {
self.writer.write_string(0, "ACDSSCHEMA")?;
self.writer.write_i32(90, 2)?;
self.writer
.write_string(1, "AcDbDs::TreatedAsObjectDataSchema")?;
self.writer
.write_string(2, "AcDbDs::TreatedAsObjectData")?;
self.writer.write_byte(280, 1)?;
self.writer.write_i32(91, 0)?;
self.writer.write_string(0, "ACDSSCHEMA")?;
self.writer.write_i32(90, 3)?;
self.writer.write_string(1, "AcDbDs::LegacySchema")?;
self.writer.write_string(2, "AcDbDs::Legacy")?;
self.writer.write_byte(280, 1)?;
self.writer.write_i32(91, 0)?;
self.writer.write_string(0, "ACDSSCHEMA")?;
self.writer.write_i32(90, 4)?;
self.writer
.write_string(1, "AcDbDs::IndexedPropertySchema")?;
self.writer.write_string(2, "AcDs:Indexable")?;
self.writer.write_byte(280, 1)?;
self.writer.write_i32(91, 0)?;
self.writer.write_string(0, "ACDSSCHEMA")?;
self.writer.write_i32(90, 5)?;
self.writer
.write_string(1, "AcDbDs::HandleAttributeSchema")?;
self.writer.write_string(2, "AcDbDs::HandleAttribute")?;
self.writer.write_byte(280, 7)?;
self.writer.write_i32(91, 1)?;
self.writer.write_i16(284, 1)?;
Ok(())
}
fn write_acds_record(
&mut self,
entity_handle: Handle,
sab_data: &[u8],
) -> Result<()> {
self.writer.write_string(0, "ACDSRECORD")?;
self.writer.write_i32(90, 1)?;
self.writer.write_string(2, "AcDbDs::ID")?;
self.writer.write_byte(280, 10)?;
self.writer.write_handle(320, entity_handle)?;
self.writer.write_string(2, "ASM_Data")?;
self.writer.write_byte(280, 15)?;
self.writer.write_i32(94, sab_data.len() as i32)?;
for chunk in sab_data.chunks(127) {
self.writer.write_binary(310, chunk)?;
}
Ok(())
}
}
fn get_invisible_edge_bits(flags: &InvisibleEdgeFlags) -> u8 {
let mut bits = 0u8;
if flags.is_first_invisible() { bits |= 1; }
if flags.is_second_invisible() { bits |= 2; }
if flags.is_third_invisible() { bits |= 4; }
if flags.is_fourth_invisible() { bits |= 8; }
bits
}
fn get_boundary_path_bits(flags: &BoundaryPathFlags) -> u32 {
let mut bits = 0u32;
if flags.is_external() { bits |= 1; }
if flags.is_polyline() { bits |= 2; }
if flags.is_derived() { bits |= 4; }
bits
}