""" Dynamic Dimensioning and Scaled Metrics Engine for PDF Exporter. Centralizes all auto-scaling calculations for grid steps, legend ticks, arrows, line widths, font sizes, and SA2 technical measure offsets. """ import math import numpy as np MM_TO_PT = 2.834645669291339 def compute_grid_steps(w_geom: float, h_geom: float) -> tuple[float, float]: """ Computes human-readable main_step (mm) and minor_step (mm) for grid overlay. """ L = max(w_geom, h_geom) if L > 1e-6: step_raw = L / 10.0 exponent = int(np.round(np.log10(step_raw))) main_step = 10.0 ** exponent else: main_step = 10.0 minor_step = main_step / 10.0 return main_step, minor_step def compute_effective_stroke_and_font( line_width_mm: float, font_size_mm: float, minor_step_mm: float ) -> tuple[float, float]: """ Calculates line_width_pt and font_size_pt proportionally based on minor_step_mm. Scales continuously from millimetric components up to large structural models. """ if line_width_mm <= 0: line_width_pt = minor_step_mm * 0.05 * MM_TO_PT else: line_width_pt = line_width_mm * MM_TO_PT if font_size_mm <= 0: font_size_pt = minor_step_mm * MM_TO_PT else: font_size_pt = font_size_mm * MM_TO_PT return line_width_pt, font_size_pt class LegendSizing: """ Auto-scaling calculations for the Size Scale Legend (bottom-left grid scale bar). """ def __init__(self, main_step_mm: float, minor_step_mm: float, line_width_pt: float, font_size_pt: float): self.main_step = main_step_mm self.minor_step = minor_step_mm self.line_width_pt = line_width_pt self.font_size_pt = font_size_pt # Tick heights in mm proportional to main grid step self.major_tick_mm = main_step_mm * 0.10 self.minor_tick_mm = main_step_mm * 0.05 # Y-axis height in mm (extends comfortably past major tick height) self.y_axis_height_mm = self.major_tick_mm * 2.0 # X-axis extension past last main step tick in mm self.x_axis_extension_mm = minor_step_mm * 2.0 # Legend Arrowhead dimensions in PT (proportional to line width) self.arrow_len_pt = max(0.5, line_width_pt * 12.0) self.arrow_half_w_pt = self.arrow_len_pt * 0.35 self.label_margin_h_pt = self.minor_tick_mm * 1.0 self.label_margin_w_pt = self.minor_tick_mm * 1.5 class SA2Sizing: """ Auto-scaling calculations for SA2 technical measurement lines and feature callouts. """ def __init__(self, w_obj_mm: float, h_obj_mm: float, line_width_pt: float, font_size_pt: float): self.w_obj = w_obj_mm self.h_obj = h_obj_mm self.line_width_pt = line_width_pt self.font_size_pt = font_size_pt self.obj_ref_size = min(w_obj_mm, h_obj_mm) if min(w_obj_mm, h_obj_mm) > 0 else max(w_obj_mm, h_obj_mm) # Dimension line offset & extension line scaling (mm) proportional to object size self.offset_mm = self.obj_ref_size * 0.10 self.ext_overhang_mm = self.offset_mm * 0.20 self.ext_gap_mm = self.offset_mm * 0.10 # Measure arrow dimensions (PT) self.arrow_len_pt = max(0.5, line_width_pt * 4.0) self.arrow_half_w_pt = self.arrow_len_pt * 0.35 def get_hole_callout_lengths(self, dia_mm: float) -> tuple[float, float]: slant_mm = dia_mm * 0.30 leader_mm = dia_mm * 0.40 return slant_mm, leader_mm def get_arc_callout_lengths(self, r_mm: float) -> tuple[float, float]: slant_mm = r_mm * 0.30 leader_mm = r_mm * 0.40 return slant_mm, leader_mm def get_chamfer_callout_lengths(self, c_val_mm: float) -> tuple[float, float]: slant_mm = c_val_mm * 1.0 leader_mm = c_val_mm * 1.5 return slant_mm, leader_mm