use crate::entities::{Entity, EntityCommon};
use crate::types::{BoundingBox3D, Color, Handle, LineWeight, Transparency, Vector3};
use bitflags::bitflags;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum CellType {
#[default]
Text = 1,
Block = 2,
}
impl From<u8> for CellType {
fn from(value: u8) -> Self {
match value {
1 => Self::Text,
2 => Self::Block,
_ => Self::Text,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u32)]
pub enum CellValueType {
#[default]
Unknown = 0,
Long = 1,
Double = 2,
String = 4,
Date = 8,
Point2D = 0x10,
Point3D = 0x20,
Handle = 0x40,
Buffer = 0x80,
ResultBuffer = 0x100,
General = 0x200,
}
impl From<u32> for CellValueType {
fn from(value: u32) -> Self {
match value {
1 => Self::Long,
2 => Self::Double,
4 => Self::String,
8 => Self::Date,
0x10 => Self::Point2D,
0x20 => Self::Point3D,
0x40 => Self::Handle,
0x80 => Self::Buffer,
0x100 => Self::ResultBuffer,
0x200 => Self::General,
_ => Self::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u32)]
pub enum ValueUnitType {
#[default]
NoUnits = 0,
Distance = 1,
Angle = 2,
Area = 4,
Volume = 8,
Currency = 0x10,
Percentage = 0x20,
}
impl From<u32> for ValueUnitType {
fn from(value: u32) -> Self {
match value {
1 => Self::Distance,
2 => Self::Angle,
4 => Self::Area,
8 => Self::Volume,
0x10 => Self::Currency,
0x20 => Self::Percentage,
_ => Self::NoUnits,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum BorderType {
#[default]
Single = 1,
Double = 2,
}
impl From<i16> for BorderType {
fn from(value: i16) -> Self {
match value {
1 => Self::Single,
2 => Self::Double,
_ => Self::Single,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum TableCellContentType {
#[default]
Unknown = 0,
Value = 1,
Field = 2,
Block = 4,
}
impl From<u8> for TableCellContentType {
fn from(value: u8) -> Self {
match value {
1 => Self::Value,
2 => Self::Field,
4 => Self::Block,
_ => Self::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum CellStyleType {
#[default]
Cell = 1,
Row = 2,
Column = 3,
FormattedTableData = 4,
Table = 5,
}
impl From<u8> for CellStyleType {
fn from(value: u8) -> Self {
match value {
1 => Self::Cell,
2 => Self::Row,
3 => Self::Column,
4 => Self::FormattedTableData,
5 => Self::Table,
_ => Self::Cell,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum BreakFlowDirection {
#[default]
Right = 1,
Vertical = 2,
Left = 4,
}
impl From<u8> for BreakFlowDirection {
fn from(value: u8) -> Self {
match value {
1 => Self::Right,
2 => Self::Vertical,
4 => Self::Left,
_ => Self::Right,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellEdgeFlags: u32 {
const NONE = 0;
const TOP = 1;
const RIGHT = 2;
const BOTTOM = 4;
const LEFT = 8;
const INSIDE_VERTICAL = 16;
const INSIDE_HORIZONTAL = 32;
const ALL = 63;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellStateFlags: u32 {
const NONE = 0;
const CONTENT_LOCKED = 1;
const CONTENT_READ_ONLY = 2;
const LINKED = 4;
const CONTENT_MODIFIED_AFTER_UPDATE = 8;
const FORMAT_LOCKED = 16;
const FORMAT_READ_ONLY = 32;
const FORMAT_MODIFIED_AFTER_UPDATE = 64;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellStylePropertyFlags: u32 {
const NONE = 0;
const DATA_TYPE = 1;
const DATA_FORMAT = 2;
const ROTATION = 4;
const BLOCK_SCALE = 8;
const ALIGNMENT = 16;
const CONTENT_COLOR = 32;
const TEXT_STYLE = 64;
const TEXT_HEIGHT = 128;
const AUTO_SCALE = 256;
const BACKGROUND_COLOR = 512;
const MARGIN_LEFT = 1024;
const MARGIN_TOP = 2048;
const MARGIN_RIGHT = 4096;
const MARGIN_BOTTOM = 8192;
const CONTENT_LAYOUT = 16384;
const MERGE_ALL = 32768;
const FLOW_DIRECTION_BOTTOM_TO_TOP = 65536;
const MARGIN_HORIZONTAL_SPACING = 131072;
const MARGIN_VERTICAL_SPACING = 262144;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BorderPropertyFlags: u32 {
const NONE = 0;
const BORDER_TYPE = 1;
const LINE_WEIGHT = 2;
const LINE_TYPE = 4;
const COLOR = 8;
const INVISIBILITY = 16;
const DOUBLE_LINE_SPACING = 32;
const ALL = 63;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ContentLayoutFlags: u32 {
const NONE = 0;
const FLOW = 1;
const STACKED_HORIZONTAL = 2;
const STACKED_VERTICAL = 4;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BreakOptionFlags: u32 {
const NONE = 0;
const ENABLE_BREAKS = 1;
const REPEAT_TOP_LABELS = 2;
const REPEAT_BOTTOM_LABELS = 4;
const ALLOW_MANUAL_POSITIONS = 8;
const ALLOW_MANUAL_HEIGHTS = 16;
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellBorder {
pub border_type: BorderType,
pub color: Color,
pub line_weight: LineWeight,
pub invisible: bool,
pub double_spacing: f64,
pub override_flags: BorderPropertyFlags,
}
impl CellBorder {
pub fn new() -> Self {
Self {
border_type: BorderType::Single,
color: Color::ByBlock,
line_weight: LineWeight::ByLayer,
invisible: false,
double_spacing: 0.0,
override_flags: BorderPropertyFlags::NONE,
}
}
pub fn with_color(color: Color) -> Self {
let mut border = Self::new();
border.color = color;
border.override_flags |= BorderPropertyFlags::COLOR;
border
}
pub fn invisible() -> Self {
let mut border = Self::new();
border.invisible = true;
border
}
}
impl Default for CellBorder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellValue {
pub value_type: CellValueType,
pub unit_type: ValueUnitType,
pub flags: i32,
pub text: String,
pub format: String,
pub formatted_value: String,
pub numeric_value: f64,
pub handle_value: Option<Handle>,
}
impl CellValue {
pub fn new() -> Self {
Self {
value_type: CellValueType::Unknown,
unit_type: ValueUnitType::NoUnits,
flags: 0,
text: String::new(),
format: String::new(),
formatted_value: String::new(),
numeric_value: 0.0,
handle_value: None,
}
}
pub fn text(s: &str) -> Self {
Self {
value_type: CellValueType::String,
unit_type: ValueUnitType::NoUnits,
flags: 0,
text: s.to_string(),
format: String::new(),
formatted_value: s.to_string(),
numeric_value: 0.0,
handle_value: None,
}
}
pub fn number(n: f64) -> Self {
Self {
value_type: CellValueType::Double,
unit_type: ValueUnitType::NoUnits,
flags: 0,
text: String::new(),
format: String::new(),
formatted_value: n.to_string(),
numeric_value: n,
handle_value: None,
}
}
pub fn integer(n: i64) -> Self {
Self {
value_type: CellValueType::Long,
unit_type: ValueUnitType::NoUnits,
flags: 0,
text: String::new(),
format: String::new(),
formatted_value: n.to_string(),
numeric_value: n as f64,
handle_value: None,
}
}
pub fn is_empty(&self) -> bool {
self.value_type == CellValueType::Unknown && self.text.is_empty()
}
pub fn display(&self) -> &str {
if !self.formatted_value.is_empty() {
&self.formatted_value
} else if !self.text.is_empty() {
&self.text
} else {
""
}
}
}
impl Default for CellValue {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellContent {
pub content_type: TableCellContentType,
pub value: CellValue,
pub block_handle: Option<Handle>,
pub text_style_handle: Option<Handle>,
pub color: Color,
pub rotation: f64,
pub scale: f64,
pub text_height: f64,
}
impl CellContent {
pub fn new() -> Self {
Self {
content_type: TableCellContentType::Unknown,
value: CellValue::new(),
block_handle: None,
text_style_handle: None,
color: Color::ByBlock,
rotation: 0.0,
scale: 1.0,
text_height: 0.18,
}
}
pub fn text(s: &str) -> Self {
Self {
content_type: TableCellContentType::Value,
value: CellValue::text(s),
block_handle: None,
text_style_handle: None,
color: Color::ByBlock,
rotation: 0.0,
scale: 1.0,
text_height: 0.18,
}
}
pub fn block(block_handle: Handle) -> Self {
Self {
content_type: TableCellContentType::Block,
value: CellValue::new(),
block_handle: Some(block_handle),
text_style_handle: None,
color: Color::ByBlock,
rotation: 0.0,
scale: 1.0,
text_height: 0.18,
}
}
}
impl Default for CellContent {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellStyle {
pub style_type: CellStyleType,
pub property_flags: CellStylePropertyFlags,
pub background_color: Color,
pub content_color: Color,
pub text_style_handle: Option<Handle>,
pub text_height: f64,
pub rotation: f64,
pub scale: f64,
pub alignment: i32,
pub fill_enabled: bool,
pub layout_flags: ContentLayoutFlags,
pub margin_left: f64,
pub margin_top: f64,
pub margin_right: f64,
pub margin_bottom: f64,
pub horizontal_spacing: f64,
pub vertical_spacing: f64,
pub top_border: CellBorder,
pub right_border: CellBorder,
pub bottom_border: CellBorder,
pub left_border: CellBorder,
}
impl CellStyle {
pub fn new() -> Self {
Self {
style_type: CellStyleType::Cell,
property_flags: CellStylePropertyFlags::NONE,
background_color: Color::ByBlock,
content_color: Color::ByBlock,
text_style_handle: None,
text_height: 0.18,
rotation: 0.0,
scale: 1.0,
alignment: 0,
fill_enabled: false,
layout_flags: ContentLayoutFlags::FLOW,
margin_left: 0.06,
margin_top: 0.06,
margin_right: 0.06,
margin_bottom: 0.06,
horizontal_spacing: 0.0,
vertical_spacing: 0.0,
top_border: CellBorder::new(),
right_border: CellBorder::new(),
bottom_border: CellBorder::new(),
left_border: CellBorder::new(),
}
}
pub fn set_margins(&mut self, margin: f64) {
self.margin_left = margin;
self.margin_top = margin;
self.margin_right = margin;
self.margin_bottom = margin;
}
pub fn set_border_color(&mut self, color: Color) {
self.top_border.color = color;
self.right_border.color = color;
self.bottom_border.color = color;
self.left_border.color = color;
}
}
impl Default for CellStyle {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableCell {
pub cell_type: CellType,
pub state: CellStateFlags,
pub contents: Vec<CellContent>,
pub style: Option<CellStyle>,
pub tooltip: String,
pub rotation: f64,
pub auto_fit: bool,
pub merge_width: i32,
pub merge_height: i32,
pub flag: i32,
pub merged: i32,
pub virtual_edge: i16,
pub has_linked_data: bool,
pub custom_data: i32,
}
impl TableCell {
pub fn new() -> Self {
Self {
cell_type: CellType::Text,
state: CellStateFlags::NONE,
contents: Vec::new(),
style: None,
tooltip: String::new(),
rotation: 0.0,
auto_fit: false,
merge_width: 1,
merge_height: 1,
flag: 0,
merged: 0,
virtual_edge: 0,
has_linked_data: false,
custom_data: 0,
}
}
pub fn text(s: &str) -> Self {
let mut cell = Self::new();
cell.contents.push(CellContent::text(s));
cell
}
pub fn block(block_handle: Handle) -> Self {
let mut cell = Self::new();
cell.cell_type = CellType::Block;
cell.contents.push(CellContent::block(block_handle));
cell
}
pub fn content(&self) -> Option<&CellContent> {
self.contents.first()
}
pub fn text_value(&self) -> &str {
self.contents.first()
.map(|c| c.value.display())
.unwrap_or("")
}
pub fn set_text(&mut self, s: &str) {
self.contents.clear();
self.contents.push(CellContent::text(s));
self.cell_type = CellType::Text;
}
pub fn is_merged(&self) -> bool {
self.merge_width > 1 || self.merge_height > 1
}
pub fn has_content(&self) -> bool {
!self.contents.is_empty()
}
pub fn has_multiple_contents(&self) -> bool {
self.contents.len() > 1
}
}
impl Default for TableCell {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableRow {
pub height: f64,
pub cells: Vec<TableCell>,
pub style: Option<CellStyle>,
pub custom_data: i32,
}
impl TableRow {
pub fn new(num_cells: usize) -> Self {
let cells = (0..num_cells).map(|_| TableCell::new()).collect();
Self {
height: 0.25,
cells,
style: None,
custom_data: 0,
}
}
pub fn from_texts(texts: &[&str]) -> Self {
let cells = texts.iter().map(|t| TableCell::text(t)).collect();
Self {
height: 0.25,
cells,
style: None,
custom_data: 0,
}
}
pub fn cell_count(&self) -> usize {
self.cells.len()
}
pub fn cell(&self, index: usize) -> Option<&TableCell> {
self.cells.get(index)
}
pub fn cell_mut(&mut self, index: usize) -> Option<&mut TableCell> {
self.cells.get_mut(index)
}
}
impl Default for TableRow {
fn default() -> Self {
Self::new(0)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableColumn {
pub name: String,
pub width: f64,
pub style: Option<CellStyle>,
pub custom_data: i32,
}
impl TableColumn {
pub fn new() -> Self {
Self {
name: String::new(),
width: 2.5,
style: None,
custom_data: 0,
}
}
pub fn with_width(width: f64) -> Self {
Self {
name: String::new(),
width,
style: None,
custom_data: 0,
}
}
pub fn named(name: &str, width: f64) -> Self {
Self {
name: name.to_string(),
width,
style: None,
custom_data: 0,
}
}
}
impl Default for TableColumn {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellRange {
pub top_row: usize,
pub left_col: usize,
pub bottom_row: usize,
pub right_col: usize,
}
impl CellRange {
pub fn cell(row: usize, col: usize) -> Self {
Self {
top_row: row,
left_col: col,
bottom_row: row,
right_col: col,
}
}
pub fn new(top_row: usize, left_col: usize, bottom_row: usize, right_col: usize) -> Self {
Self {
top_row,
left_col,
bottom_row,
right_col,
}
}
pub fn row_count(&self) -> usize {
self.bottom_row - self.top_row + 1
}
pub fn col_count(&self) -> usize {
self.right_col - self.left_col + 1
}
pub fn cell_count(&self) -> usize {
self.row_count() * self.col_count()
}
pub fn contains(&self, row: usize, col: usize) -> bool {
row >= self.top_row && row <= self.bottom_row &&
col >= self.left_col && col <= self.right_col
}
}
impl Default for CellRange {
fn default() -> Self {
Self::cell(0, 0)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Table {
pub common: EntityCommon,
pub insertion_point: Vector3,
pub horizontal_direction: Vector3,
pub normal: Vector3,
pub table_style_handle: Option<Handle>,
pub block_record_handle: Option<Handle>,
pub data_version: i16,
pub value_flags: i32,
pub override_flag: bool,
pub override_border_color: bool,
pub override_border_line_weight: bool,
pub override_border_visibility: bool,
pub rows: Vec<TableRow>,
pub columns: Vec<TableColumn>,
pub break_options: BreakOptionFlags,
pub break_flow_direction: BreakFlowDirection,
pub break_spacing: f64,
}
impl Table {
pub fn new(insertion_point: Vector3, num_rows: usize, num_cols: usize) -> Self {
let rows = (0..num_rows).map(|_| TableRow::new(num_cols)).collect();
let columns = (0..num_cols).map(|_| TableColumn::new()).collect();
Self {
common: EntityCommon::default(),
insertion_point,
horizontal_direction: Vector3::new(1.0, 0.0, 0.0),
normal: Vector3::new(0.0, 0.0, 1.0),
table_style_handle: None,
block_record_handle: None,
data_version: 0,
value_flags: 0,
override_flag: false,
override_border_color: false,
override_border_line_weight: false,
override_border_visibility: false,
rows,
columns,
break_options: BreakOptionFlags::NONE,
break_flow_direction: BreakFlowDirection::Right,
break_spacing: 0.0,
}
}
pub fn row_count(&self) -> usize {
self.rows.len()
}
pub fn column_count(&self) -> usize {
self.columns.len()
}
pub fn cell(&self, row: usize, col: usize) -> Option<&TableCell> {
self.rows.get(row).and_then(|r| r.cells.get(col))
}
pub fn cell_mut(&mut self, row: usize, col: usize) -> Option<&mut TableCell> {
self.rows.get_mut(row).and_then(|r| r.cells.get_mut(col))
}
pub fn set_cell_text(&mut self, row: usize, col: usize, text: &str) -> bool {
if let Some(cell) = self.cell_mut(row, col) {
cell.set_text(text);
true
} else {
false
}
}
pub fn cell_text(&self, row: usize, col: usize) -> Option<&str> {
self.cell(row, col).map(|c| c.text_value())
}
pub fn set_row_height(&mut self, row: usize, height: f64) {
if let Some(r) = self.rows.get_mut(row) {
r.height = height;
}
}
pub fn set_column_width(&mut self, col: usize, width: f64) {
if let Some(c) = self.columns.get_mut(col) {
c.width = width;
}
}
pub fn total_width(&self) -> f64 {
self.columns.iter().map(|c| c.width).sum()
}
pub fn total_height(&self) -> f64 {
self.rows.iter().map(|r| r.height).sum()
}
pub fn add_row(&mut self) -> &mut TableRow {
let num_cols = self.columns.len();
self.rows.push(TableRow::new(num_cols));
self.rows.last_mut().unwrap()
}
pub fn add_column(&mut self, width: f64) {
self.columns.push(TableColumn::with_width(width));
for row in &mut self.rows {
row.cells.push(TableCell::new());
}
}
pub fn insert_row(&mut self, index: usize) {
let num_cols = self.columns.len();
if index <= self.rows.len() {
self.rows.insert(index, TableRow::new(num_cols));
}
}
pub fn insert_column(&mut self, index: usize, width: f64) {
if index <= self.columns.len() {
self.columns.insert(index, TableColumn::with_width(width));
for row in &mut self.rows {
row.cells.insert(index, TableCell::new());
}
}
}
pub fn remove_row(&mut self, index: usize) -> Option<TableRow> {
if index < self.rows.len() {
Some(self.rows.remove(index))
} else {
None
}
}
pub fn remove_column(&mut self, index: usize) -> Option<TableColumn> {
if index < self.columns.len() {
let col = self.columns.remove(index);
for row in &mut self.rows {
if index < row.cells.len() {
row.cells.remove(index);
}
}
Some(col)
} else {
None
}
}
pub fn merge_cells(&mut self, range: CellRange) {
if let Some(cell) = self.cell_mut(range.top_row, range.left_col) {
cell.merge_width = range.col_count() as i32;
cell.merge_height = range.row_count() as i32;
}
}
pub fn unmerge_cell(&mut self, row: usize, col: usize) {
if let Some(cell) = self.cell_mut(row, col) {
cell.merge_width = 1;
cell.merge_height = 1;
}
}
pub fn set_uniform_row_height(&mut self, height: f64) {
for row in &mut self.rows {
row.height = height;
}
}
pub fn set_uniform_column_width(&mut self, width: f64) {
for col in &mut self.columns {
col.width = width;
}
}
pub fn clear_content(&mut self) {
for row in &mut self.rows {
for cell in &mut row.cells {
cell.contents.clear();
}
}
}
}
impl Default for Table {
fn default() -> Self {
Self::new(Vector3::ZERO, 3, 3)
}
}
impl Entity for Table {
fn handle(&self) -> Handle {
self.common.handle
}
fn set_handle(&mut self, handle: Handle) {
self.common.handle = handle;
}
fn layer(&self) -> &str {
&self.common.layer
}
fn set_layer(&mut self, layer: String) {
self.common.layer = layer;
}
fn color(&self) -> Color {
self.common.color
}
fn set_color(&mut self, color: Color) {
self.common.color = color;
}
fn line_weight(&self) -> LineWeight {
self.common.line_weight
}
fn set_line_weight(&mut self, line_weight: LineWeight) {
self.common.line_weight = line_weight;
}
fn transparency(&self) -> Transparency {
self.common.transparency
}
fn set_transparency(&mut self, transparency: Transparency) {
self.common.transparency = transparency;
}
fn is_invisible(&self) -> bool {
self.common.invisible
}
fn set_invisible(&mut self, invisible: bool) {
self.common.invisible = invisible;
}
fn bounding_box(&self) -> BoundingBox3D {
let min = self.insertion_point;
let max = Vector3::new(
min.x + self.total_width(),
min.y + self.total_height(),
min.z,
);
BoundingBox3D::new(min, max)
}
fn translate(&mut self, offset: Vector3) {
super::translate::translate_table(self, offset);
}
fn entity_type(&self) -> &'static str {
"ACAD_TABLE"
}
fn apply_transform(&mut self, transform: &crate::types::Transform) {
super::transform::transform_table(self, transform);
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableBuilder {
table: Table,
}
impl TableBuilder {
pub fn new(rows: usize, cols: usize) -> Self {
Self {
table: Table::new(Vector3::ZERO, rows, cols),
}
}
pub fn at(mut self, point: Vector3) -> Self {
self.table.insertion_point = point;
self
}
pub fn row_height(mut self, height: f64) -> Self {
self.table.set_uniform_row_height(height);
self
}
pub fn column_width(mut self, width: f64) -> Self {
self.table.set_uniform_column_width(width);
self
}
pub fn layer(mut self, layer: &str) -> Self {
self.table.common.layer = layer.to_string();
self
}
pub fn cell_text(mut self, row: usize, col: usize, text: &str) -> Self {
self.table.set_cell_text(row, col, text);
self
}
pub fn header(mut self, headers: &[&str]) -> Self {
for (col, text) in headers.iter().enumerate() {
self.table.set_cell_text(0, col, text);
}
self
}
pub fn build(self) -> Table {
self.table
}
}
impl Default for TableBuilder {
fn default() -> Self {
Self::new(3, 3)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_table_creation() {
let table = Table::new(Vector3::ZERO, 3, 4);
assert_eq!(table.row_count(), 3);
assert_eq!(table.column_count(), 4);
}
#[test]
fn test_table_cell_access() {
let mut table = Table::new(Vector3::ZERO, 2, 2);
table.set_cell_text(0, 0, "A1");
table.set_cell_text(1, 1, "B2");
assert_eq!(table.cell_text(0, 0), Some("A1"));
assert_eq!(table.cell_text(1, 1), Some("B2"));
assert_eq!(table.cell_text(0, 1), Some(""));
}
#[test]
fn test_table_dimensions() {
let mut table = Table::new(Vector3::ZERO, 2, 3);
table.set_uniform_row_height(1.0);
table.set_uniform_column_width(2.0);
assert_eq!(table.total_height(), 2.0);
assert_eq!(table.total_width(), 6.0);
}
#[test]
fn test_table_add_row() {
let mut table = Table::new(Vector3::ZERO, 2, 2);
table.add_row();
assert_eq!(table.row_count(), 3);
assert_eq!(table.rows[2].cell_count(), 2);
}
#[test]
fn test_table_add_column() {
let mut table = Table::new(Vector3::ZERO, 2, 2);
table.add_column(3.0);
assert_eq!(table.column_count(), 3);
assert_eq!(table.columns[2].width, 3.0);
assert_eq!(table.rows[0].cell_count(), 3);
}
#[test]
fn test_table_remove_row() {
let mut table = Table::new(Vector3::ZERO, 3, 2);
table.remove_row(1);
assert_eq!(table.row_count(), 2);
}
#[test]
fn test_table_remove_column() {
let mut table = Table::new(Vector3::ZERO, 2, 3);
table.remove_column(1);
assert_eq!(table.column_count(), 2);
assert_eq!(table.rows[0].cell_count(), 2);
}
#[test]
fn test_cell_merge() {
let mut table = Table::new(Vector3::ZERO, 3, 3);
table.merge_cells(CellRange::new(0, 0, 1, 2));
let cell = table.cell(0, 0).unwrap();
assert!(cell.is_merged());
assert_eq!(cell.merge_width, 3);
assert_eq!(cell.merge_height, 2);
}
#[test]
fn test_cell_value() {
let text = CellValue::text("Hello");
assert_eq!(text.display(), "Hello");
assert!(!text.is_empty());
let number = CellValue::number(42.5);
assert_eq!(number.numeric_value, 42.5);
}
#[test]
fn test_cell_range() {
let range = CellRange::new(1, 2, 3, 4);
assert_eq!(range.row_count(), 3);
assert_eq!(range.col_count(), 3);
assert_eq!(range.cell_count(), 9);
assert!(range.contains(2, 3));
assert!(!range.contains(0, 0));
}
#[test]
fn test_table_builder() {
let table = TableBuilder::new(2, 3)
.at(Vector3::new(10.0, 20.0, 0.0))
.row_height(0.5)
.column_width(2.0)
.header(&["A", "B", "C"])
.cell_text(1, 0, "Data")
.build();
assert_eq!(table.insertion_point.x, 10.0);
assert_eq!(table.cell_text(0, 0), Some("A"));
assert_eq!(table.cell_text(1, 0), Some("Data"));
}
#[test]
fn test_table_bounding_box() {
let mut table = Table::new(Vector3::new(5.0, 10.0, 0.0), 2, 3);
table.set_uniform_row_height(1.0);
table.set_uniform_column_width(2.0);
let bbox = table.bounding_box();
assert_eq!(bbox.min.x, 5.0);
assert_eq!(bbox.min.y, 10.0);
assert_eq!(bbox.max.x, 11.0); assert_eq!(bbox.max.y, 12.0); }
#[test]
fn test_table_translate() {
let mut table = Table::new(Vector3::new(0.0, 0.0, 0.0), 2, 2);
table.translate(Vector3::new(10.0, 20.0, 5.0));
assert_eq!(table.insertion_point, Vector3::new(10.0, 20.0, 5.0));
}
#[test]
fn test_cell_content() {
let text = CellContent::text("Hello");
assert_eq!(text.content_type, TableCellContentType::Value);
let block = CellContent::block(Handle::new(0x100));
assert_eq!(block.content_type, TableCellContentType::Block);
assert!(block.block_handle.is_some());
}
#[test]
fn test_cell_border() {
let border = CellBorder::with_color(Color::from_index(1));
assert!(!border.invisible);
assert!(border.override_flags.contains(BorderPropertyFlags::COLOR));
}
#[test]
fn test_cell_style() {
let mut style = CellStyle::new();
style.set_margins(0.1);
style.set_border_color(Color::from_index(2));
assert_eq!(style.margin_left, 0.1);
assert_eq!(style.top_border.color, Color::from_index(2));
}
#[test]
fn test_table_row() {
let row = TableRow::from_texts(&["A", "B", "C"]);
assert_eq!(row.cell_count(), 3);
assert_eq!(row.cell(0).unwrap().text_value(), "A");
}
#[test]
fn test_table_column() {
let col = TableColumn::named("Width", 5.0);
assert_eq!(col.name, "Width");
assert_eq!(col.width, 5.0);
}
#[test]
fn test_insert_operations() {
let mut table = Table::new(Vector3::ZERO, 2, 2);
table.insert_row(1);
assert_eq!(table.row_count(), 3);
table.insert_column(1, 3.0);
assert_eq!(table.column_count(), 3);
assert_eq!(table.rows[0].cell_count(), 3);
}
#[test]
fn test_cell_state_flags() {
let flags = CellStateFlags::CONTENT_LOCKED | CellStateFlags::FORMAT_LOCKED;
assert!(flags.contains(CellStateFlags::CONTENT_LOCKED));
assert!(!flags.contains(CellStateFlags::LINKED));
}
}