处理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