This commit is contained in:
Christian Anetzberger
2026-01-22 20:23:51 +01:00
commit a197de9456
4327 changed files with 1235205 additions and 0 deletions

View File

@@ -0,0 +1,219 @@
# 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.58125074e5 ly = 4.84813681e6 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}>")