处理is_invisible和标注文本位置
commit
5ce0de699a
@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
ko_fi: vadoola
|
||||
buy_me_a_coffee: vadoola
|
||||
@ -0,0 +1,79 @@
|
||||
name: Package
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu
|
||||
arch: i386
|
||||
target: i686-unknown-linux-gnu
|
||||
- os: ubuntu
|
||||
arch: armhf
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
- os: ubuntu
|
||||
arch: amd64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: ubuntu
|
||||
arch: arm64
|
||||
target: aarch64-unknown-linux-gnu
|
||||
- os: macos
|
||||
arch: amd64
|
||||
target: x86_64-apple-darwin
|
||||
- os: macos
|
||||
arch: arm64
|
||||
target: aarch64-apple-darwin
|
||||
- os: windows
|
||||
arch: i386
|
||||
target: i686-pc-windows-msvc
|
||||
- os: windows
|
||||
arch: amd64
|
||||
target: x86_64-pc-windows-msvc
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Check crate
|
||||
if: matrix.os == 'macos' || matrix.os == 'windows' || matrix.os == 'ubuntu' && matrix.arch == 'amd64'
|
||||
run: cargo publish --dry-run --target ${{ matrix.target }}
|
||||
|
||||
- name: Clippy (release mode)
|
||||
run: cargo clippy --release -- -D warnings
|
||||
|
||||
- name: Test (release mode)
|
||||
if: matrix.os == 'macos' || matrix.os == 'ubuntu' || matrix.os == 'windows' && matrix.arch == 'amd64'
|
||||
run: |
|
||||
cargo test --release --verbose -- --nocapture &&
|
||||
cargo clean
|
||||
|
||||
- name: Install Cross
|
||||
if: matrix.os == 'ubuntu'
|
||||
run: cargo install cross --git https://github.com/cross-rs/cross
|
||||
|
||||
- name: Build binary (Linux)
|
||||
if: matrix.os == 'ubuntu'
|
||||
run: cross build --release --target ${{ matrix.target }}
|
||||
|
||||
- name: Build binary (macOS/Windows)
|
||||
if: matrix.os == 'macos' || matrix.os == 'windows'
|
||||
run: cargo build --release --target ${{ matrix.target }}
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{ matrix.os }}-${{ matrix.target }}
|
||||
path: |
|
||||
target/*/release/dxf2elmt
|
||||
target/*/release/dxf2elmt.exe
|
||||
if-no-files-found: error
|
||||
@ -0,0 +1,43 @@
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["[0-9]+.[0-9]+.[0-9]+*"]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
# Linters inspired from here: https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md
|
||||
jobs:
|
||||
rust:
|
||||
name: ${{ matrix.os }}-latest
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu
|
||||
- os: macos
|
||||
- os: windows
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: fmt
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
- name: build
|
||||
if: matrix.os != 'windows' || github.event_name != 'pull_request'
|
||||
run: cargo build --verbose
|
||||
|
||||
- name: clippy
|
||||
if: matrix.os != 'windows' || github.event_name != 'pull_request'
|
||||
#run: cargo clippy -- -D warnings
|
||||
run: cargo clippy --
|
||||
|
||||
- name: test
|
||||
if: matrix.os != 'windows' || github.event_name != 'pull_request'
|
||||
run: cargo test --verbose -- --nocapture
|
||||
@ -0,0 +1,11 @@
|
||||
/target
|
||||
Dockerfile
|
||||
*.dxf
|
||||
*.elmt
|
||||
cross.toml
|
||||
/.cargo
|
||||
*.txt
|
||||
*.exe
|
||||
/.vscode
|
||||
Cargo.lock
|
||||
.DS_Store
|
||||
@ -0,0 +1,50 @@
|
||||
[package]
|
||||
name = "dxf2elmt"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
description = "A CLI program to convert .dxf files into .elmt files"
|
||||
authors = ["Vadoola <github: vadoola>", "Antonio Aguilar <github: antonioaja>"]
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Vadoola/dxf2elmt"
|
||||
license = "MIT"
|
||||
rust-version = "1.79.0"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
lto = true
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
dxf = "0.6.0"
|
||||
simple-xml-builder = "1.1.0"
|
||||
bspline = "1.1.0"
|
||||
uuid = { version = "1.16", features = ["serde", "v4"] }
|
||||
tempfile = "3.15"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
anyhow = "1.0.97"
|
||||
wild = "2.2"
|
||||
rayon = "1.10.0"
|
||||
hex_color = "3.0.0"
|
||||
itertools = "0.14"
|
||||
parley = "0.2.0"
|
||||
unicode-segmentation = "1.12.0"
|
||||
tracing = "0.1"
|
||||
venator = { version = "1.1", optional = true }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
#[lints.clippy]
|
||||
#unwrap_used = "deny"
|
||||
|
||||
|
||||
#undecided on how I want to handle logging in this crate right now.
|
||||
#I do want to do a bit of tracing debugging on some of these recursive blocks though
|
||||
#for now I'll add tracing and venator under a trace feature that is disabled by default
|
||||
#maybe a log feature which uses trace to log to a text file or syslog or something
|
||||
#and a seperate feature that logs to venator? Also figure out how best to isolate
|
||||
#it to a module, so I don' thave #[cfg(feature = ...)] all over the place
|
||||
#https://www.shuttle.dev/blog/2024/01/09/getting-started-tracing-rust#instrumentation-in-tracing
|
||||
[features]
|
||||
default = []
|
||||
venator = ["dep:venator"]
|
||||
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Antonio Aguilar
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@ -0,0 +1,58 @@
|
||||
# dxf2elmt
|
||||
dxf2elmt is CLI program which can convert .dxf files into [QElectroTech](https://qelectrotech.org/) .elmt files. The program supports both ascii and binary .dxf files.
|
||||
|
||||
The goal of this program is to create a fast and accurate conversion tool to be used with [QElectroTech](https://qelectrotech.org/).
|
||||
|
||||
## How to Use
|
||||
dxf2elmt requires only one input from the user, the input file.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
./dxf2elmt my_file.dxf
|
||||
```
|
||||
|
||||
The .elmt file will be output into the same directory as the executable. It will retain the name of the .dxf file.
|
||||
|
||||
If you wish to forgo creating an .elmt file, you can use the "-v" argument for verbose output. This will output the contents of the .elmt file to stdout without actually creating the file. For example:
|
||||
|
||||
```bash
|
||||
./dxf2elmt my_file.dxf -v
|
||||
```
|
||||
|
||||
## Supported Entities
|
||||
|
||||
* Lines
|
||||
* Circles
|
||||
* Arcs
|
||||
* Texts
|
||||
* Ellipses
|
||||
* Polylines
|
||||
* LwPolylines
|
||||
* Solids
|
||||
* Splines
|
||||
* Blocks (there are still some known issues for deeply nested block)
|
||||
* MText (partial support)
|
||||
* Leader
|
||||
|
||||
## To Do
|
||||
|
||||
* Support for the following
|
||||
* Remaining 2d entities
|
||||
* Styling (such as Dimension Styles)
|
||||
|
||||
* Better error messages
|
||||
* Logging
|
||||
|
||||
## Compiling
|
||||
|
||||
Compiled using Rust (MSRV 1.79.0).
|
||||
|
||||
## Credits
|
||||
|
||||
* [Antonioaja](https://github.com/antonioaja) for creating the initial versions of [dxf2elmt](https://github.com/antonioaja/dxf2elmt). Thank you for all your work.
|
||||
* [QElectroTech](https://qelectrotech.org/)
|
||||
* [dxf-rs](https://github.com/IxMilia/dxf-rs)
|
||||
* [simple-xml-builder](https://github.com/Accelbread/simple-xml-builder)
|
||||
* [bspline](https://github.com/Twinklebear/bspline)
|
||||
* [tempfile](https://github.com/Stebalien/tempfile)
|
||||
@ -0,0 +1,26 @@
|
||||
extern crate tempfile;
|
||||
|
||||
use anyhow::Context;
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tempfile::tempfile;
|
||||
|
||||
pub fn create_file(
|
||||
verbose_output: bool,
|
||||
_info: bool,
|
||||
file_name: &Path,
|
||||
) -> Result<File, anyhow::Error> {
|
||||
let old_file_name = file_name.to_string_lossy();
|
||||
|
||||
let mut file_name = PathBuf::from(file_name);
|
||||
file_name.set_extension("elmt");
|
||||
|
||||
let friendly_file_name = file_name.to_string_lossy();
|
||||
let mut out_file = tempfile().context("Could not create temporary file");
|
||||
if !verbose_output {
|
||||
out_file = File::create(&file_name).context("Could not create output file");
|
||||
println!("{friendly_file_name} was created... \nNow converting {old_file_name}...",);
|
||||
}
|
||||
|
||||
out_file.context("Could not return output file")
|
||||
}
|
||||
@ -0,0 +1,207 @@
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::pedantic,
|
||||
//clippy::cargo,
|
||||
//rust_2024_compatibility,
|
||||
)]
|
||||
//#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
||||
|
||||
extern crate dxf;
|
||||
extern crate simple_xml_builder;
|
||||
extern crate unicode_segmentation;
|
||||
|
||||
use anyhow::{Context, Ok, Result};
|
||||
use clap::Parser;
|
||||
use dxf::entities::EntityType;
|
||||
use dxf::Drawing;
|
||||
use qelmt::Definition;
|
||||
//use rayon::prelude::*;
|
||||
use simple_xml_builder::XMLElement;
|
||||
use std::time::Instant;
|
||||
use std::{io, path::PathBuf};
|
||||
use tracing::{span, trace, warn, Level};
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
#[cfg(feature = "venator")]
|
||||
use venator::Venator;
|
||||
|
||||
mod qelmt;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "dxf2elmt")]
|
||||
#[command(author, version, about = "A CLI program to convert .dxf files into .elmt files", long_about = None)]
|
||||
struct Args {
|
||||
/// The .dxf file to convert
|
||||
//#[clap(short, long, value_parser)]
|
||||
file_names: Vec<PathBuf>,
|
||||
|
||||
/// Activates verbose output, eliminates .elmt file writing
|
||||
#[clap(short, long, value_parser, default_value_t = false)]
|
||||
verbose: bool,
|
||||
|
||||
/// Converts text entities into dynamic text instead of the default text box
|
||||
#[clap(short, long, value_parser, default_value_t = false)]
|
||||
dtext: bool,
|
||||
|
||||
/// Determine the number of lines you want each spline to have (more lines = greater resolution)
|
||||
#[clap(short, long, value_parser, default_value_t = 20)]
|
||||
spline_step: u32,
|
||||
|
||||
/// Toggles information output... defaults to off
|
||||
#[clap(short, long, value_parser, default_value_t = false)]
|
||||
info: bool,
|
||||
}
|
||||
|
||||
pub mod file_writer;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn main() -> Result<()> {
|
||||
#[cfg(feature = "venator")]
|
||||
let tr_reg = tracing_subscriber::registry()
|
||||
.with(Venator::default())
|
||||
.with(tracing_subscriber::fmt::Layer::default());
|
||||
|
||||
#[cfg(not(feature = "venator"))]
|
||||
let tr_reg = {
|
||||
let file_layer =
|
||||
if let std::result::Result::Ok(file) = std::fs::File::create("dxf2elmt.log") {
|
||||
//if we can create a log file use it
|
||||
Some(tracing_subscriber::fmt::layer().with_writer(std::sync::Arc::new(file)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let stde_layer = tracing_subscriber::fmt::layer()
|
||||
.pretty()
|
||||
.with_writer(io::stderr);
|
||||
tracing_subscriber::registry()
|
||||
.with(file_layer.map(|fl| {
|
||||
fl.with_file(true)
|
||||
.with_line_number(true)
|
||||
.with_thread_ids(true)
|
||||
.with_ansi(false)
|
||||
.with_filter(tracing_subscriber::EnvFilter::from_env("DXF2E_LOG"))
|
||||
}))
|
||||
.with(
|
||||
stde_layer
|
||||
.with_file(true)
|
||||
.with_line_number(true)
|
||||
.with_thread_ids(true)
|
||||
.with_filter(tracing_subscriber::EnvFilter::from_env("DXF2E_LOG")),
|
||||
)
|
||||
};
|
||||
tr_reg.init();
|
||||
|
||||
trace!("Starting dxf2elmt");
|
||||
|
||||
// Start recording time
|
||||
let now: Instant = Instant::now();
|
||||
|
||||
// Collect arguments
|
||||
let args: Args = Args::parse_from(wild::args());
|
||||
|
||||
// Load dxf file
|
||||
let dxf_loop_span = span!(Level::TRACE, "Looping over dxf files");
|
||||
let dxf_loop_guard = dxf_loop_span.enter();
|
||||
for file_name in args.file_names {
|
||||
let friendly_file_name = file_name
|
||||
.file_stem()
|
||||
.unwrap_or_else(|| file_name.as_os_str())
|
||||
.to_string_lossy();
|
||||
let drawing: Drawing = Drawing::load_file(&file_name).context(format!(
|
||||
"Failed to load {friendly_file_name}...\n\tMake sure the file is a valid .dxf file.",
|
||||
))?;
|
||||
let q_elmt = Definition::new(friendly_file_name.clone(), args.spline_step, &drawing);
|
||||
if !args.verbose && args.info {
|
||||
println!("{friendly_file_name} loaded...");
|
||||
}
|
||||
|
||||
// Initialize counts
|
||||
let mut circle_count: u32 = 0;
|
||||
let mut line_count: u32 = 0;
|
||||
let mut arc_count: u32 = 0;
|
||||
let mut spline_count: u32 = 0;
|
||||
let mut text_count: u32 = 0;
|
||||
let mut ellipse_count: u32 = 0;
|
||||
let mut polyline_count: u32 = 0;
|
||||
let mut lwpolyline_count: u32 = 0;
|
||||
let mut solid_count: u32 = 0;
|
||||
let mut block_count: u32 = 0;
|
||||
let mut other_count: u32 = 0;
|
||||
|
||||
// Loop through all entities, counting the element types
|
||||
//drawing.entities().for_each(|e| match e.specific {
|
||||
drawing.entities().for_each(|e| match e.specific {
|
||||
EntityType::Circle(ref _circle) => {
|
||||
circle_count += 1;
|
||||
}
|
||||
EntityType::Line(ref _line) => {
|
||||
line_count += 1;
|
||||
}
|
||||
EntityType::Arc(ref _arc) => {
|
||||
arc_count += 1;
|
||||
}
|
||||
EntityType::Spline(ref _spline) => {
|
||||
spline_count += 1;
|
||||
}
|
||||
EntityType::Text(ref _text) => {
|
||||
text_count += 1;
|
||||
}
|
||||
EntityType::Ellipse(ref _ellipse) => {
|
||||
ellipse_count += 1;
|
||||
}
|
||||
EntityType::Polyline(ref _polyline) => {
|
||||
polyline_count += 1;
|
||||
}
|
||||
EntityType::LwPolyline(ref _lwpolyline) => {
|
||||
lwpolyline_count += 1;
|
||||
}
|
||||
EntityType::Solid(ref _solid) => {
|
||||
solid_count += 1;
|
||||
}
|
||||
EntityType::Insert(ref _insert) => {
|
||||
block_count += 1;
|
||||
}
|
||||
_ => {
|
||||
other_count += 1;
|
||||
}
|
||||
});
|
||||
|
||||
// Create output file for .elmt
|
||||
let out_file = file_writer::create_file(args.verbose, args.info, &file_name)?;
|
||||
|
||||
// Write to output file
|
||||
let out_xml = XMLElement::from(&q_elmt);
|
||||
out_xml
|
||||
.write(&out_file)
|
||||
.context("Failed to write output file.")?;
|
||||
|
||||
if args.info {
|
||||
println!("Conversion complete!\n");
|
||||
|
||||
// Print stats
|
||||
println!("STATS");
|
||||
println!("~~~~~~~~~~~~~~~");
|
||||
println!("Circles: {circle_count}");
|
||||
println!("Lines: {line_count}");
|
||||
println!("Arcs: {arc_count}");
|
||||
println!("Splines: {spline_count}");
|
||||
println!("Texts: {text_count}");
|
||||
println!("Ellipses: {ellipse_count}");
|
||||
println!("Polylines: {polyline_count}");
|
||||
println!("LwPolylines: {lwpolyline_count}");
|
||||
println!("Solids: {solid_count}");
|
||||
println!("Blocks: {block_count}");
|
||||
println!("Currently Unsupported: {other_count}");
|
||||
|
||||
println!("\nTime Elapsed: {} ms", now.elapsed().as_millis());
|
||||
}
|
||||
|
||||
if args.verbose {
|
||||
print!("{out_xml}");
|
||||
}
|
||||
}
|
||||
drop(dxf_loop_guard);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
use super::{two_dec, ScaleEntity};
|
||||
use dxf::entities;
|
||||
use simple_xml_builder::XMLElement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Arc {
|
||||
//need to brush up on my Rust scoping rules, isn't there a way to make this pub to just the module?
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
|
||||
width: f64,
|
||||
height: f64,
|
||||
start: f64,
|
||||
angle: f64,
|
||||
style: String,
|
||||
antialias: bool,
|
||||
}
|
||||
|
||||
impl From<&entities::Arc> for Arc {
|
||||
fn from(arc: &entities::Arc) -> Self {
|
||||
let temp_angle = if arc.start_angle > arc.end_angle {
|
||||
(360.0 - arc.start_angle) + arc.end_angle
|
||||
} else {
|
||||
arc.end_angle - arc.start_angle
|
||||
};
|
||||
|
||||
Arc {
|
||||
x: arc.center.x - arc.radius,
|
||||
y: -arc.center.y - arc.radius,
|
||||
height: arc.radius * 2.0,
|
||||
width: arc.radius * 2.0,
|
||||
start: if arc.start_angle < 0.0 {
|
||||
-arc.start_angle
|
||||
} else {
|
||||
arc.start_angle
|
||||
},
|
||||
angle: if temp_angle < 0.0 {
|
||||
-temp_angle
|
||||
} else {
|
||||
temp_angle
|
||||
},
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: if arc.thickness > 0.1 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Arc> for XMLElement {
|
||||
fn from(arc: &Arc) -> Self {
|
||||
let mut arc_xml: XMLElement = XMLElement::new("arc");
|
||||
arc_xml.add_attribute("x", two_dec(arc.x));
|
||||
arc_xml.add_attribute("y", two_dec(arc.y));
|
||||
arc_xml.add_attribute("width", two_dec(arc.width));
|
||||
arc_xml.add_attribute("height", two_dec(arc.height));
|
||||
arc_xml.add_attribute("start", arc.start.round());
|
||||
arc_xml.add_attribute("angle", arc.angle.round());
|
||||
arc_xml.add_attribute("antialias", arc.antialias);
|
||||
arc_xml.add_attribute("style", &arc.style);
|
||||
arc_xml
|
||||
}
|
||||
}
|
||||
|
||||
impl ScaleEntity for Arc {
|
||||
fn scale(&mut self, fact_x: f64, fact_y: f64) {
|
||||
self.x *= fact_x;
|
||||
self.y *= fact_y;
|
||||
self.width *= fact_x;
|
||||
self.height *= fact_y;
|
||||
}
|
||||
|
||||
fn left_bound(&self) -> f64 {
|
||||
self.x
|
||||
}
|
||||
|
||||
fn right_bound(&self) -> f64 {
|
||||
self.x + self.width
|
||||
}
|
||||
|
||||
fn top_bound(&self) -> f64 {
|
||||
self.y
|
||||
}
|
||||
|
||||
fn bot_bound(&self) -> f64 {
|
||||
self.y + self.height
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
use super::{two_dec, Circularity, ScaleEntity};
|
||||
use dxf::entities::{self, Circle, LwPolyline, Polyline};
|
||||
use simple_xml_builder::XMLElement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ellipse {
|
||||
height: f64,
|
||||
width: f64,
|
||||
style: String,
|
||||
|
||||
//need to brush up on my Rust scoping rules, isn't there a way to make this pub to just the module?
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
|
||||
antialias: bool,
|
||||
}
|
||||
|
||||
impl From<&Circle> for Ellipse {
|
||||
fn from(circ: &Circle) -> Self {
|
||||
Ellipse {
|
||||
x: circ.center.x - circ.radius,
|
||||
y: -circ.center.y - circ.radius,
|
||||
height: circ.radius * 2.0,
|
||||
width: circ.radius * 2.0,
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: if circ.thickness > 0.5 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&entities::Ellipse> for Ellipse {
|
||||
fn from(ellipse: &entities::Ellipse) -> Self {
|
||||
Ellipse {
|
||||
x: ellipse.center.x - ellipse.major_axis.x,
|
||||
y: -ellipse.center.y - ellipse.major_axis.x * ellipse.minor_axis_ratio,
|
||||
height: ellipse.major_axis.x * 2.0,
|
||||
width: ellipse.major_axis.x * 2.0 * ellipse.minor_axis_ratio,
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: "line-style:normal;line-weight:thin;filling:none;color:black".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Polyline> for Ellipse {
|
||||
type Error = &'static str; //add better error later
|
||||
|
||||
fn try_from(poly: &Polyline) -> Result<Self, Self::Error> {
|
||||
if !poly.is_circular() {
|
||||
return Err("Polyline has poor circularity, can't convert");
|
||||
}
|
||||
|
||||
//I did this fold because min requires the vertex to have the Ordering trait
|
||||
//but I forogot min_by exists taking a lambda, so I could compare them using
|
||||
//the value I need. However my first quick attemp wasn't working
|
||||
//Using min_by would probably be more effecietn than the fold
|
||||
//So this is probably worth coming back to...but it's a low priority
|
||||
//because the below code works.
|
||||
let x = poly
|
||||
.vertices()
|
||||
.fold(f64::MAX, |min_x, vtx| min_x.min(vtx.location.x));
|
||||
|
||||
let max_x = poly
|
||||
.vertices()
|
||||
.fold(f64::MIN, |max_x, vtx| max_x.max(vtx.location.x));
|
||||
|
||||
let y = poly
|
||||
.vertices()
|
||||
.fold(f64::MAX, |min_y, vtx| min_y.min(vtx.location.y));
|
||||
|
||||
let max_y = poly
|
||||
.vertices()
|
||||
.fold(f64::MIN, |max_y, vtx| max_y.max(vtx.location.y));
|
||||
|
||||
Ok(Ellipse {
|
||||
x,
|
||||
y: -max_y,
|
||||
height: max_y - y,
|
||||
width: max_x - x,
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: "line-style:normal;line-weight:thin;filling:none;color:black".into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&LwPolyline> for Ellipse {
|
||||
type Error = &'static str; //add better error later
|
||||
|
||||
fn try_from(poly: &LwPolyline) -> Result<Self, Self::Error> {
|
||||
if !poly.is_circular() {
|
||||
return Err("Polyline has poor circularity, can't convert");
|
||||
}
|
||||
|
||||
let x = poly
|
||||
.vertices
|
||||
.iter()
|
||||
.fold(f64::MAX, |min_x, vtx| min_x.min(vtx.x));
|
||||
|
||||
let max_x = poly
|
||||
.vertices
|
||||
.iter()
|
||||
.fold(f64::MIN, |max_x, vtx| max_x.max(vtx.x));
|
||||
|
||||
let y = poly
|
||||
.vertices
|
||||
.iter()
|
||||
.fold(f64::MAX, |min_y, vtx| min_y.min(vtx.y));
|
||||
|
||||
let max_y = poly
|
||||
.vertices
|
||||
.iter()
|
||||
.fold(f64::MIN, |max_y, vtx| max_y.max(vtx.y));
|
||||
|
||||
Ok(Ellipse {
|
||||
x,
|
||||
y: -max_y,
|
||||
height: max_y - y,
|
||||
width: max_x - x,
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: "line-style:normal;line-weight:thin;filling:none;color:black".into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Ellipse> for XMLElement {
|
||||
fn from(ell: &Ellipse) -> Self {
|
||||
let mut ell_xml: XMLElement = XMLElement::new("ellipse");
|
||||
ell_xml.add_attribute("x", two_dec(ell.x));
|
||||
ell_xml.add_attribute("y", two_dec(ell.y));
|
||||
ell_xml.add_attribute("width", two_dec(ell.width));
|
||||
ell_xml.add_attribute("height", two_dec(ell.height));
|
||||
ell_xml.add_attribute("antialias", ell.antialias);
|
||||
ell_xml.add_attribute("style", &ell.style);
|
||||
ell_xml
|
||||
}
|
||||
}
|
||||
|
||||
impl ScaleEntity for Ellipse {
|
||||
fn scale(&mut self, fact_x: f64, fact_y: f64) {
|
||||
self.x *= fact_x;
|
||||
self.y *= fact_y;
|
||||
self.width *= fact_x;
|
||||
self.height *= fact_y;
|
||||
}
|
||||
|
||||
fn left_bound(&self) -> f64 {
|
||||
self.x
|
||||
}
|
||||
|
||||
fn right_bound(&self) -> f64 {
|
||||
self.x + self.width
|
||||
}
|
||||
|
||||
fn top_bound(&self) -> f64 {
|
||||
self.y
|
||||
}
|
||||
|
||||
fn bot_bound(&self) -> f64 {
|
||||
self.y + self.height
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,211 @@
|
||||
use super::two_dec;
|
||||
use super::LineEnd;
|
||||
use super::ScaleEntity;
|
||||
use dxf::entities::{self, LwPolyline, Polyline};
|
||||
use simple_xml_builder::XMLElement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Line {
|
||||
length2: f64,
|
||||
end2: LineEnd,
|
||||
length1: f64,
|
||||
|
||||
//need to brush up on my Rust scoping rules, isn't there a way to make this pub to just the module?
|
||||
pub x1: f64,
|
||||
pub y1: f64,
|
||||
pub x2: f64,
|
||||
pub y2: f64,
|
||||
|
||||
style: String,
|
||||
end1: LineEnd,
|
||||
antialias: bool,
|
||||
}
|
||||
|
||||
pub struct Leader(pub Vec<Line>);
|
||||
|
||||
impl From<&entities::Line> for Line {
|
||||
fn from(line: &entities::Line) -> Self {
|
||||
Line {
|
||||
x1: line.p1.x,
|
||||
y1: -line.p1.y,
|
||||
length1: 1.5, //why is this statically set at 1.5?
|
||||
end1: LineEnd::None,
|
||||
x2: line.p2.x,
|
||||
y2: -line.p2.y,
|
||||
length2: 1.5, //why is this statically set at 1.5?
|
||||
end2: LineEnd::None,
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: if line.thickness > 0.5 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Polyline> for Line {
|
||||
type Error = &'static str; //add better error later
|
||||
|
||||
fn try_from(poly: &Polyline) -> Result<Self, Self::Error> {
|
||||
if poly.__vertices_and_handles.len() != 2 {
|
||||
return Err("Error can't convert polyline with more than 2 points into a Line");
|
||||
}
|
||||
|
||||
Ok(Line {
|
||||
x1: poly.__vertices_and_handles[0].0.location.x,
|
||||
y1: -poly.__vertices_and_handles[0].0.location.y,
|
||||
length1: 1.5, //why is this statically set at 1.5?
|
||||
end1: LineEnd::None,
|
||||
x2: poly.__vertices_and_handles[1].0.location.x,
|
||||
y2: -poly.__vertices_and_handles[1].0.location.y,
|
||||
length2: 1.5, //why is this statically set at 1.5?
|
||||
end2: LineEnd::None,
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: if poly.thickness > 0.5 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&LwPolyline> for Line {
|
||||
type Error = &'static str; //add better error later
|
||||
|
||||
fn try_from(poly: &LwPolyline) -> Result<Self, Self::Error> {
|
||||
if poly.vertices.len() != 2 {
|
||||
return Err("Error can't convert polyline with more than 2 points into a Line");
|
||||
}
|
||||
|
||||
Ok(Line {
|
||||
x1: poly.vertices[0].x,
|
||||
y1: -poly.vertices[0].y,
|
||||
length1: 1.5, //why is this statically set at 1.5?
|
||||
end1: LineEnd::None,
|
||||
x2: poly.vertices[1].x,
|
||||
y2: -poly.vertices[1].y,
|
||||
length2: 1.5, //why is this statically set at 1.5?
|
||||
end2: LineEnd::None,
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: if poly.thickness > 0.1 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&entities::Leader> for Leader {
|
||||
fn from(leader: &entities::Leader) -> Self {
|
||||
Leader(
|
||||
leader
|
||||
.vertices
|
||||
.windows(2)
|
||||
.enumerate()
|
||||
.map(|(cnt, pt_slice)| {
|
||||
let end1 = if leader.use_arrowheads && cnt == 0 {
|
||||
LineEnd::SimpleArrow
|
||||
} else {
|
||||
LineEnd::None
|
||||
};
|
||||
|
||||
Line {
|
||||
x1: pt_slice[0].x,
|
||||
y1: -pt_slice[0].y,
|
||||
length1: 1.5, //In order to get the arrow sizing, I need to read in the dimension styling first
|
||||
end1,
|
||||
x2: pt_slice[1].x,
|
||||
y2: -pt_slice[1].y,
|
||||
length2: 1.5, //In order to get the arrow sizing, I need to read in the dimension styling first
|
||||
end2: LineEnd::None,
|
||||
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
//looks like line thickenss and color information I *might* need to grab from a dimension style
|
||||
//entity which I haven't implemented yet
|
||||
/*style: if line.thickness > 0.5 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),*/
|
||||
style: "line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
.into(),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Line> for XMLElement {
|
||||
fn from(line: &Line) -> Self {
|
||||
let mut line_xml: XMLElement = XMLElement::new("line");
|
||||
line_xml.add_attribute("x1", two_dec(line.x1));
|
||||
line_xml.add_attribute("y1", two_dec(line.y1));
|
||||
line_xml.add_attribute("length1", two_dec(line.length1));
|
||||
line_xml.add_attribute("end1", &line.end1);
|
||||
line_xml.add_attribute("x2", two_dec(line.x2));
|
||||
line_xml.add_attribute("y2", two_dec(line.y2));
|
||||
line_xml.add_attribute("length2", two_dec(line.length2));
|
||||
line_xml.add_attribute("end2", &line.end2);
|
||||
line_xml.add_attribute("antialias", line.antialias);
|
||||
line_xml.add_attribute("style", &line.style);
|
||||
line_xml
|
||||
}
|
||||
}
|
||||
|
||||
impl ScaleEntity for Line {
|
||||
fn scale(&mut self, fact_x: f64, fact_y: f64) {
|
||||
self.x1 *= fact_x;
|
||||
self.x2 *= fact_x;
|
||||
|
||||
self.y1 *= fact_y;
|
||||
self.y2 *= fact_y;
|
||||
|
||||
//while writing this scaling code, I'm looking at
|
||||
//QET_ElementScaler from plc-user to see if there are
|
||||
//any easy to overlook mistakes that I might make
|
||||
//doing the scaling. It seems they limit these lengths
|
||||
//to 99.0, but I'm not sure why at the moment. I'll go
|
||||
//ahead and limit them as well, and try to come back to
|
||||
//figure out what the purpose here is
|
||||
self.length1 *= fact_x.min(fact_y);
|
||||
self.length1 = self.length1.min(99.0);
|
||||
|
||||
self.length2 *= fact_x.min(fact_y);
|
||||
self.length2 = self.length2.min(99.0);
|
||||
}
|
||||
|
||||
fn left_bound(&self) -> f64 {
|
||||
self.x1.min(self.x2)
|
||||
}
|
||||
|
||||
fn right_bound(&self) -> f64 {
|
||||
self.x1.max(self.x2)
|
||||
}
|
||||
|
||||
fn top_bound(&self) -> f64 {
|
||||
self.y1.min(self.y2)
|
||||
}
|
||||
|
||||
fn bot_bound(&self) -> f64 {
|
||||
self.y1.max(self.y2)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,282 @@
|
||||
use super::{two_dec, ScaleEntity};
|
||||
use dxf::entities::{LwPolyline, Polyline, Solid, Spline};
|
||||
use simple_xml_builder::XMLElement;
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
//wait Why do I have a coordinate AND a Point struct, that are
|
||||
//essentially the same. It's been a couple of months, but I'm not
|
||||
//seeing why I would have done this....almost makes me wondering
|
||||
//if I started, then stopped, and then didn't realize where I left off
|
||||
//and started again but used a different name...?
|
||||
//Might need to take a closer look and clean this up.
|
||||
#[derive(Debug)]
|
||||
pub struct Coordinate {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Point {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
impl Point {
|
||||
pub fn new(x: f64, y: f64) -> Point {
|
||||
Point { x, y }
|
||||
}
|
||||
}
|
||||
impl Mul<f64> for Point {
|
||||
type Output = Point;
|
||||
fn mul(self, rhs: f64) -> Point {
|
||||
Point {
|
||||
x: self.x * rhs,
|
||||
y: self.y * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Add for Point {
|
||||
type Output = Point;
|
||||
fn add(self, rhs: Point) -> Point {
|
||||
Point {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Polygon {
|
||||
style: String,
|
||||
antialias: bool,
|
||||
pub coordinates: Vec<Coordinate>,
|
||||
closed: bool,
|
||||
}
|
||||
|
||||
impl From<&Polyline> for Polygon {
|
||||
fn from(poly: &Polyline) -> Self {
|
||||
Polygon {
|
||||
coordinates: poly
|
||||
.__vertices_and_handles
|
||||
.iter()
|
||||
.map(|(vertex, _handle)| Coordinate {
|
||||
x: vertex.location.x,
|
||||
y: -vertex.location.y,
|
||||
})
|
||||
.collect(),
|
||||
closed: poly.is_closed(),
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: if poly.thickness > 0.1 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&LwPolyline> for Polygon {
|
||||
fn from(poly: &LwPolyline) -> Self {
|
||||
Polygon {
|
||||
coordinates: poly
|
||||
.vertices
|
||||
.iter()
|
||||
.map(|vertex| Coordinate {
|
||||
x: vertex.x,
|
||||
y: -vertex.y,
|
||||
})
|
||||
.collect(),
|
||||
closed: poly.is_closed(),
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: if poly.thickness > 0.1 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&Spline, u32)> for Polygon {
|
||||
fn from((spline, spline_step): (&Spline, u32)) -> Self {
|
||||
let mut i: usize = 0;
|
||||
let mut points: Vec<Point> = Vec::new();
|
||||
for _a in &spline.control_points {
|
||||
points.push(Point::new(
|
||||
spline.control_points[i].x,
|
||||
spline.control_points[i].y,
|
||||
));
|
||||
i += 1;
|
||||
}
|
||||
i = 0;
|
||||
let mut knots: Vec<f64> = Vec::new();
|
||||
for _a in &spline.knot_values {
|
||||
knots.push(spline.knot_values[i]);
|
||||
i += 1;
|
||||
}
|
||||
let curr_spline = bspline::BSpline::new(
|
||||
spline.degree_of_curve.unsigned_abs() as usize,
|
||||
points,
|
||||
knots,
|
||||
);
|
||||
let step: f64 =
|
||||
(curr_spline.knot_domain().1 - curr_spline.knot_domain().0) / (spline_step as f64);
|
||||
|
||||
//there is probably a way to clean up some of this logic and use iterators
|
||||
//although it looks like step_by doesn't work on a f64 range...hmmm
|
||||
//but I haven't inspected it too closely, and for now am pretty much just duplicating
|
||||
//it as antonioaja had it
|
||||
let coordinates = {
|
||||
let mut coords = Vec::with_capacity(
|
||||
((curr_spline.knot_domain().1 - curr_spline.knot_domain().0) / step) as usize + 1,
|
||||
);
|
||||
let mut j: f64 = curr_spline.knot_domain().0;
|
||||
i = 0;
|
||||
while j < curr_spline.knot_domain().1 {
|
||||
coords.push(Coordinate {
|
||||
x: curr_spline.point(j).x,
|
||||
y: -curr_spline.point(j).y,
|
||||
});
|
||||
j += step;
|
||||
i += 1;
|
||||
}
|
||||
coords
|
||||
};
|
||||
|
||||
Polygon {
|
||||
coordinates,
|
||||
closed: spline.is_closed(),
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: "line-style:normal;line-weight:thin;filling:none;color:black".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Solid> for Polygon {
|
||||
fn from(solid: &Solid) -> Self {
|
||||
Polygon {
|
||||
coordinates: vec![
|
||||
Coordinate {
|
||||
x: solid.first_corner.x,
|
||||
y: -solid.first_corner.y,
|
||||
},
|
||||
Coordinate {
|
||||
x: solid.second_corner.x,
|
||||
y: -solid.second_corner.y,
|
||||
},
|
||||
Coordinate {
|
||||
x: solid.third_corner.x,
|
||||
y: -solid.third_corner.y,
|
||||
},
|
||||
Coordinate {
|
||||
x: solid.fourth_corner.x,
|
||||
y: -solid.fourth_corner.y,
|
||||
},
|
||||
],
|
||||
closed: true,
|
||||
//in the original code antialias is always set to false...I'm guessing for performance
|
||||
//reasons...I'm trying to think if there is a time we might want to turn it on?
|
||||
antialias: false,
|
||||
style: if solid.thickness > 0.5 {
|
||||
"line-style:normal;line-weight:normal;filling:none;color:black"
|
||||
} else {
|
||||
"line-style:normal;line-weight:thin;filling:none;color:black"
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Polygon> for XMLElement {
|
||||
fn from(poly: &Polygon) -> Self {
|
||||
let mut poly_xml: XMLElement = XMLElement::new("polygon");
|
||||
|
||||
for (count, coord) in poly.coordinates.iter().enumerate() {
|
||||
poly_xml.add_attribute(format!("x{}", (count + 1)), two_dec(coord.x));
|
||||
poly_xml.add_attribute(format!("y{}", (count + 1)), two_dec(coord.y));
|
||||
}
|
||||
|
||||
//closed defaults to true, don't need to write it out unless it's false
|
||||
if !poly.closed {
|
||||
poly_xml.add_attribute("closed", poly.closed);
|
||||
}
|
||||
|
||||
poly_xml.add_attribute("antialias", poly.antialias);
|
||||
poly_xml.add_attribute("style", &poly.style);
|
||||
poly_xml
|
||||
}
|
||||
}
|
||||
|
||||
impl ScaleEntity for Polygon {
|
||||
fn scale(&mut self, fact_x: f64, fact_y: f64) {
|
||||
self.coordinates.iter_mut().for_each(|coord| {
|
||||
coord.x *= fact_x;
|
||||
coord.y *= fact_y;
|
||||
});
|
||||
}
|
||||
|
||||
fn left_bound(&self) -> f64 {
|
||||
let min_coord = self.coordinates.iter().min_by(|c1, c2| {
|
||||
//if we get a None for the compare, then just returns Greater which will ignore it
|
||||
//for finding the minimum
|
||||
c1.x.partial_cmp(&c2.x)
|
||||
.unwrap_or(std::cmp::Ordering::Greater)
|
||||
});
|
||||
|
||||
if let Some(min_coord) = min_coord {
|
||||
min_coord.x
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn right_bound(&self) -> f64 {
|
||||
let max_coord = self.coordinates.iter().max_by(|c1, c2| {
|
||||
//if we get a None for the compare, then just returns Less which will ignore it
|
||||
//for finding the maximum
|
||||
c1.x.partial_cmp(&c2.x).unwrap_or(std::cmp::Ordering::Less)
|
||||
});
|
||||
|
||||
if let Some(max_coord) = max_coord {
|
||||
max_coord.x
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn top_bound(&self) -> f64 {
|
||||
let min_coord = self.coordinates.iter().min_by(|c1, c2| {
|
||||
//if we get a None for the compare, then just returns Greater which will ignore it
|
||||
//for finding the minimum
|
||||
c1.y.partial_cmp(&c2.y)
|
||||
.unwrap_or(std::cmp::Ordering::Greater)
|
||||
});
|
||||
|
||||
if let Some(min_coord) = min_coord {
|
||||
min_coord.y
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn bot_bound(&self) -> f64 {
|
||||
let max_coord = self.coordinates.iter().max_by(|c1, c2| {
|
||||
//if we get a None for the compare, then just returns Less which will ignore it
|
||||
//for finding the maximum
|
||||
c1.y.partial_cmp(&c2.y).unwrap_or(std::cmp::Ordering::Less)
|
||||
});
|
||||
|
||||
if let Some(max_coord) = max_coord {
|
||||
max_coord.y
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
use super::{two_dec, FontInfo, ScaleEntity};
|
||||
use dxf::entities;
|
||||
use hex_color::HexColor;
|
||||
use simple_xml_builder::XMLElement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Text {
|
||||
rotation: f64,
|
||||
value: String,
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
font: FontInfo,
|
||||
color: HexColor,
|
||||
}
|
||||
|
||||
impl From<(&entities::Text, HexColor)> for Text {
|
||||
fn from((txt, color): (&entities::Text, HexColor)) -> Self {
|
||||
Text {
|
||||
x: txt.location.x,
|
||||
y: -txt.location.y,
|
||||
rotation: if txt.rotation.abs().round() as i64 % 360 != 0 {
|
||||
txt.rotation - 180.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
color,
|
||||
font: if &txt.text_style_name == "STANDARD" {
|
||||
FontInfo::default()
|
||||
} else {
|
||||
//txt.text_style_name.clone()
|
||||
FontInfo::default()
|
||||
},
|
||||
value: txt.value.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Text> for XMLElement {
|
||||
fn from(txt: &Text) -> Self {
|
||||
let mut txt_xml: XMLElement = XMLElement::new("text");
|
||||
txt_xml.add_attribute("x", two_dec(txt.x));
|
||||
txt_xml.add_attribute("y", two_dec(txt.y));
|
||||
txt_xml.add_attribute("rotation", two_dec(txt.rotation));
|
||||
txt_xml.add_attribute("color", txt.color.display_rgb());
|
||||
txt_xml.add_attribute("font", &txt.font);
|
||||
txt_xml.add_attribute("text", &txt.value);
|
||||
txt_xml
|
||||
}
|
||||
}
|
||||
|
||||
impl ScaleEntity for Text {
|
||||
fn scale(&mut self, fact_x: f64, fact_y: f64) {
|
||||
self.x *= fact_x;
|
||||
self.y *= fact_y;
|
||||
//self.font.pixel_size *= fact;
|
||||
self.font.point_size *= fact_x;
|
||||
}
|
||||
|
||||
fn left_bound(&self) -> f64 {
|
||||
self.x
|
||||
}
|
||||
|
||||
fn top_bound(&self) -> f64 {
|
||||
self.y
|
||||
}
|
||||
|
||||
fn right_bound(&self) -> f64 {
|
||||
//need to be able to measure text size to get this
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn bot_bound(&self) -> f64 {
|
||||
//need to be able to measure text size to get this
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue