220 lines
5.4 KiB
Python
220 lines
5.4 KiB
Python
# Copyright (c) 2019-2023 Manfred Moitzi
|
||
# License: MIT License
|
||
from typing import Optional
|
||
from ezdxf.enums import InsertUnits
|
||
|
||
# Documentation: https://ezdxf.mozman.at/docs/concepts/units.html#insunits
|
||
|
||
MSP_METRIC_UNITS_FACTORS = {
|
||
# length in units / factor = length in meters
|
||
# length in meters * factor = length in units
|
||
"km": 0.001,
|
||
"m": 1.0,
|
||
"dm": 10.0,
|
||
"cm": 100.0,
|
||
"mm": 1000.0,
|
||
"µm": 1000000.0,
|
||
"yd": 1.093613298,
|
||
"ft": 3.280839895,
|
||
"in": 39.37007874,
|
||
"mi": 0.00062137119,
|
||
}
|
||
|
||
IN = 1
|
||
FT = 2
|
||
MI = 3
|
||
MM = 4
|
||
CM = 5
|
||
M = 6
|
||
KM = 7
|
||
YD = 10
|
||
DM = 14
|
||
|
||
IMPERIAL_UNITS = {
|
||
InsertUnits.Inches,
|
||
InsertUnits.Feet,
|
||
InsertUnits.Miles,
|
||
InsertUnits.Microinches,
|
||
InsertUnits.Mils,
|
||
InsertUnits.Yards,
|
||
InsertUnits.USSurveyFeet,
|
||
InsertUnits.USSurveyInch,
|
||
InsertUnits.USSurveyYard,
|
||
InsertUnits.USSurveyMile,
|
||
}
|
||
|
||
# Conversion factor from meters to unit
|
||
# 1 meter is ... [unit]
|
||
METER_FACTOR = [
|
||
None, # 0 = Unitless - not supported
|
||
39.37007874, # 1 = Inches
|
||
3.280839895, # 2 = Feet
|
||
0.00062137119, # 3 = Miles
|
||
1000.0, # 4 = Millimeters
|
||
100.0, # 5 = Centimeters
|
||
1.0, # 6 = Meters
|
||
0.001, # 7 = Kilometers
|
||
None, # 8 = Microinches = 1e-6 in
|
||
None, # 9 = Mils = 0.001 in
|
||
1.093613298, # 10 = Yards
|
||
10000000000.0, # 11 = Angstroms = 1e-10m
|
||
1000000000.0, # 12 = Nanometers = 1e-9m
|
||
1000000.0, # 13 = Microns = 1e-6m
|
||
10.0, # 14 = Decimeters = 0.1m
|
||
0.1, # 15 = Decameters = 10m
|
||
0.01, # 16 = Hectometers = 100m
|
||
0.000000001, # 17 = Gigameters = 1e+9 m
|
||
1.0 / 149597870700, # 18 = Astronomical units = 149597870700m
|
||
1.0 / 9.46e15, # 19 = Light years = 9.46e15 m
|
||
1.0 / 3.09e16, # 20 = Parsecs = 3.09e16 m
|
||
None, # 21 = US Survey Feet
|
||
None, # 22 = US Survey Inch
|
||
None, # 23 = US Survey Yard
|
||
None, # 24 = US Survey Mile
|
||
]
|
||
|
||
|
||
class DrawingUnits:
|
||
def __init__(self, base: float = 1.0, unit: str = "m"):
|
||
self.base = float(base)
|
||
self.unit = unit.lower()
|
||
self._units = MSP_METRIC_UNITS_FACTORS
|
||
self._msp_factor = base * self._units[self.unit]
|
||
|
||
def factor(self, unit: str = "m") -> float:
|
||
return self._msp_factor / self._units[unit.lower()]
|
||
|
||
def __call__(self, unit: str) -> float:
|
||
return self.factor(unit)
|
||
|
||
|
||
class PaperSpaceUnits:
|
||
def __init__(self, msp=DrawingUnits(), unit: str = "mm", scale: float = 1):
|
||
self.unit = unit.lower()
|
||
self.scale = scale
|
||
self._msp = msp
|
||
self._psp = DrawingUnits(1, self.unit)
|
||
|
||
def from_msp(self, value: float, unit: str):
|
||
drawing_units = value * self._msp(unit.lower())
|
||
return drawing_units / (self._msp(self.unit) * self.scale)
|
||
|
||
def to_msp(self, value: float, unit: str):
|
||
paper_space_units = value * self.scale * self._psp.factor(unit)
|
||
model_space_units = paper_space_units * self._msp.factor(self.unit)
|
||
return model_space_units
|
||
|
||
|
||
# Layout units are stored as enum in the associated BLOCK_RECORD: BlockRecord.dxf.units
|
||
# or as optional XDATA for all DXF versions
|
||
# 1000: "ACAD"
|
||
# 1001: "DesignCenter Data" (optional)
|
||
# 1002: "{"
|
||
# 1070: Autodesk Design Center version number
|
||
# 1070: Insert units: like 'units'
|
||
# 1002: "}"
|
||
|
||
# The units of the modelspace block record is always 0, the real modelspace
|
||
# units and therefore the document units are stored as enum in the header var
|
||
# $INSUNITS
|
||
|
||
# units stored as enum in BlockRecord.dxf.units
|
||
# 0 = Unitless
|
||
# 1 = Inches
|
||
# 2 = Feet
|
||
# 3 = Miles
|
||
# 4 = Millimeters
|
||
# 5 = Centimeters
|
||
# 6 = Meters
|
||
# 7 = Kilometers
|
||
# 8 = Microinches = 1e-6 in
|
||
# 9 = Mils = 0.001 in
|
||
# 10 = Yards
|
||
# 11 = Angstroms = 1e-10m
|
||
# 12 = Nanometers = 1e-9m
|
||
# 13 = Microns = 1e-6m
|
||
# 14 = Decimeters = 0.1m
|
||
# 15 = Decameters = 10m
|
||
# 16 = Hectometers = 100m
|
||
# 17 = Gigameters = 1e+9 m
|
||
# 18 = Astronomical units = 149597870700m = 1.58125074e−5 ly = 4.84813681e−6 Parsec
|
||
# 19 = Light years = 9.46e15 m
|
||
# 20 = Parsecs = 3.09e16 m
|
||
# 21 = US Survey Feet
|
||
# 22 = US Survey Inch
|
||
# 23 = US Survey Yard
|
||
# 24 = US Survey Mile
|
||
_unit_spec = [
|
||
None,
|
||
"in",
|
||
"ft",
|
||
"mi",
|
||
"mm",
|
||
"cm",
|
||
"m",
|
||
"km",
|
||
"µin",
|
||
"mil",
|
||
"yd",
|
||
"Å",
|
||
"nm",
|
||
"µm",
|
||
"dm",
|
||
"dam",
|
||
"hm",
|
||
"gm",
|
||
"au",
|
||
"ly",
|
||
"pc",
|
||
None,
|
||
None,
|
||
None,
|
||
None,
|
||
]
|
||
|
||
|
||
def decode(enum: int) -> Optional[str]:
|
||
return _unit_spec[int(enum)]
|
||
|
||
|
||
def conversion_factor(
|
||
source_units: InsertUnits, target_units: InsertUnits
|
||
) -> float:
|
||
"""Returns the conversion factor to represent `source_units` in
|
||
`target_units`.
|
||
|
||
E.g. millimeter in centimeter :code:`conversion_factor(MM, CM)` returns 0.1,
|
||
because 1 mm = 0.1 cm
|
||
|
||
"""
|
||
try:
|
||
source_factor = METER_FACTOR[source_units]
|
||
target_factor = METER_FACTOR[target_units]
|
||
if source_factor is None or target_factor is None:
|
||
raise TypeError("Unsupported conversion.")
|
||
return target_factor / source_factor
|
||
|
||
except IndexError:
|
||
raise ValueError("Invalid unit enum.")
|
||
|
||
|
||
def unit_name(enum: int) -> str:
|
||
"""Returns the name of the unit enum."""
|
||
try:
|
||
return InsertUnits(enum).name
|
||
except ValueError:
|
||
return "unitless"
|
||
|
||
|
||
ANGLE_UNITS = {
|
||
0: "Decimal Degrees",
|
||
1: "Degrees/Minutes/Seconds",
|
||
2: "Grad",
|
||
3: "Radians",
|
||
}
|
||
|
||
|
||
def angle_unit_name(enum: int) -> str:
|
||
"""Returns the name of the angle unit enum."""
|
||
return ANGLE_UNITS.get(enum, f"unknown unit <{enum}>")
|