use crate::description::parse_helper::Parse;
use crate::description::util::IterUtil;
use crate::fixture_type::FixtureType;
use crate::model::Model;
use crate::physical_descriptions::Emitter;
use crate::validation::{ValidationError, ValidationErrorType, ValidationObject, ValidationResult};
use crate::values::{DmxAddress, Matrix, Name, Node, NodeExt, Vector3};
use serde::{Deserialize, Serialize};
pub trait AnyGeometry {
fn name(&self) -> Option<&Name>;
fn model_name(&self) -> Option<&Name>;
fn children(&self) -> &[Geometry];
fn model<'s>(&self, parent_fixture_type: &'s FixtureType) -> Option<&'s Model> {
parent_fixture_type.model(self.model_name()?)
}
fn child(&self, name: &str) -> Option<&Geometry> {
self.children()
.iter()
.find(|child| child.name().map(Name::as_ref) == Some(name))
}
fn nested_child(&self, name: &str) -> Option<&Geometry> {
for child in self.children() {
if child.name().map(Name::as_ref) == Some(name) {
return Some(child);
};
if let Some(nested_child) = child.nested_child(name) {
return Some(nested_child);
}
}
None
}
fn child_node(&self, node: &[Name]) -> Option<&Geometry> {
let (name, tail) = node.split_first()?;
let geometry = self.child(name)?;
if tail.is_empty() {
Some(geometry)
} else {
geometry.child_node(tail)
}
}
#[doc(hidden)]
fn validate_custom(&self, _parent_fixture_type: &FixtureType, _result: &mut ValidationResult) {}
fn validate(&self, parent_fixture_type: &FixtureType, result: &mut ValidationResult) {
if self.name().is_none() {
result.errors.push(ValidationError::new(
ValidationObject::Geometry,
None,
ValidationErrorType::MissingName,
));
}
if let (Some(model_name), None) = (self.model_name(), self.model(parent_fixture_type)) {
result.errors.push(ValidationError::new(
ValidationObject::Geometry,
self.name().map(Name::to_string),
ValidationErrorType::LinkNotFound(
ValidationObject::Model,
Node::new([model_name.clone()]),
),
));
}
self.validate_custom(parent_fixture_type, result);
for child in self.children() {
child.validate(parent_fixture_type, result);
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Geometry {
#[serde(rename = "Geometry")]
Generic(GenericGeometry),
Axis(AxisGeometry),
FilterBeam(BeamFilterGeometry),
FilterColor(ColorFilterGeometry),
FilterGobo(GoboFilterGeometry),
FilterShaper(ShaperFilterGeometry),
Beam(BeamGeometry),
MediaServerLayer(MediaServerLayerGeometry),
MediaServerCamera(MediaServerCameraGeometry),
MediaServerMaster(MediaServerMasterGeometry),
Display(DisplayGeometry),
#[serde(rename = "GeometryReference")]
Reference(ReferenceGeometry),
Laser(LaserGeometry),
WiringObject(WiringObjectGeometry),
Inventory(InventoryGeometry),
Structure(StructureGeometry),
Support(SupportGeometry),
Magnet(MagnetGeometry),
}
impl AnyGeometry for Geometry {
fn name(&self) -> Option<&Name> {
match self {
Geometry::Generic(g) => g.name(),
Geometry::Axis(g) => g.name(),
Geometry::FilterBeam(g) => g.name(),
Geometry::FilterColor(g) => g.name(),
Geometry::FilterGobo(g) => g.name(),
Geometry::FilterShaper(g) => g.name(),
Geometry::Beam(g) => g.name(),
Geometry::MediaServerLayer(g) => g.name(),
Geometry::MediaServerCamera(g) => g.name(),
Geometry::MediaServerMaster(g) => g.name(),
Geometry::Display(g) => g.name(),
Geometry::Reference(g) => g.name(),
Geometry::Laser(g) => g.name(),
Geometry::WiringObject(g) => g.name(),
Geometry::Inventory(g) => g.name(),
Geometry::Structure(g) => g.name(),
Geometry::Support(g) => g.name(),
Geometry::Magnet(g) => g.name(),
}
}
fn model_name(&self) -> Option<&Name> {
match self {
Geometry::Generic(g) => g.model_name(),
Geometry::Axis(g) => g.model_name(),
Geometry::FilterBeam(g) => g.model_name(),
Geometry::FilterColor(g) => g.model_name(),
Geometry::FilterGobo(g) => g.model_name(),
Geometry::FilterShaper(g) => g.model_name(),
Geometry::Beam(g) => g.model_name(),
Geometry::MediaServerLayer(g) => g.model_name(),
Geometry::MediaServerCamera(g) => g.model_name(),
Geometry::MediaServerMaster(g) => g.model_name(),
Geometry::Display(g) => g.model_name(),
Geometry::Reference(g) => g.model_name(),
Geometry::Laser(g) => g.model_name(),
Geometry::WiringObject(g) => g.model_name(),
Geometry::Inventory(g) => g.model_name(),
Geometry::Structure(g) => g.model_name(),
Geometry::Support(g) => g.model_name(),
Geometry::Magnet(g) => g.model_name(),
}
}
fn children(&self) -> &[Geometry] {
match self {
Geometry::Generic(g) => g.children(),
Geometry::Axis(g) => g.children(),
Geometry::FilterBeam(g) => g.children(),
Geometry::FilterColor(g) => g.children(),
Geometry::FilterGobo(g) => g.children(),
Geometry::FilterShaper(g) => g.children(),
Geometry::Beam(g) => g.children(),
Geometry::MediaServerLayer(g) => g.children(),
Geometry::MediaServerCamera(g) => g.children(),
Geometry::MediaServerMaster(g) => g.children(),
Geometry::Display(g) => g.children(),
Geometry::Reference(g) => g.children(),
Geometry::Laser(g) => g.children(),
Geometry::WiringObject(g) => g.children(),
Geometry::Inventory(g) => g.children(),
Geometry::Structure(g) => g.children(),
Geometry::Support(g) => g.children(),
Geometry::Magnet(g) => g.children(),
}
}
}
macro_rules! impl_any_geometry {
($str:ident) => {
impl AnyGeometry for $str {
fn name(&self) -> Option<&Name> {
self.name.as_ref()
}
fn model_name(&self) -> Option<&Name> {
self.model.as_ref()
}
fn children(&self) -> &[Geometry] {
&self.children
}
}
};
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GenericGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(GenericGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AxisGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(AxisGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BeamFilterGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(BeamFilterGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ColorFilterGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(ColorFilterGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GoboFilterGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(GoboFilterGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ShaperFilterGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(ShaperFilterGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BeamGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
#[serde(rename = "@LampType", default)]
pub lamp_type: LampType,
#[serde(rename = "@PowerConsumption", default = "default_power_consumption")]
pub power_consumption: f64,
#[serde(rename = "@LuminousFlux", default = "default_luminous_flux")]
pub luminous_flux: f64,
#[serde(rename = "@ColorTemperature", default = "default_color_temperature")]
pub color_temperature: f64,
#[serde(rename = "@BeamAngle", default = "default_beam_angle")]
pub beam_angle: f64,
#[serde(rename = "@FieldAngle", default = "default_field_angle")]
pub field_angle: f64,
#[serde(rename = "@ThrowRatio", default = "default_throw_ratio")]
pub throw_ratio: f64,
#[serde(rename = "@RectangleRatio", default = "default_rectangle_ratio")]
pub rectangle_ratio: f64,
#[serde(rename = "@BeamRadius", default = "default_beam_radius")]
pub beam_radius: f64,
#[serde(rename = "@BeamType", default)]
pub beam_type: BeamType,
#[serde(
rename = "@ColorRenderingIndex",
default = "default_color_rendering_index"
)]
pub color_rendering_index: u8,
#[serde(rename = "@EmitterSpectrum", skip_serializing_if = "Option::is_none")]
pub emitter_spectrum: Option<Node>,
}
impl AnyGeometry for BeamGeometry {
fn name(&self) -> Option<&Name> {
self.name.as_ref()
}
fn model_name(&self) -> Option<&Name> {
self.model.as_ref()
}
fn children(&self) -> &[Geometry] {
&self.children
}
fn validate_custom(&self, parent_fixture_type: &FixtureType, result: &mut ValidationResult) {
if let (Some(emitter_spectrum), None) = (
&self.emitter_spectrum,
self.emitter_spectrum(parent_fixture_type),
) {
result.errors.push(ValidationError::new(
ValidationObject::Geometry,
self.name().map(Name::to_string),
ValidationErrorType::LinkNotFound(
ValidationObject::Emitter,
emitter_spectrum.clone(),
),
));
}
}
}
impl BeamGeometry {
pub fn emitter_spectrum<'s>(
&self,
parent_fixture_type: &'s FixtureType,
) -> Option<&'s Emitter> {
let name = self.emitter_spectrum.as_ref()?.single()?;
parent_fixture_type.physical_descriptions.emitter(name)
}
}
fn default_color_temperature() -> f64 {
6000.
}
fn default_color_rendering_index() -> u8 {
100
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum LampType {
Tungsten,
Halogen,
#[serde(rename = "LED")]
Led,
#[default]
#[serde(other)]
Discharge,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum BeamType {
Spot,
None,
Rectangle,
#[serde(rename = "PC")]
Pc,
Fresnel,
Glow,
#[default]
#[serde(other)]
Wash,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MediaServerLayerGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(MediaServerLayerGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MediaServerCameraGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(MediaServerCameraGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MediaServerMasterGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(MediaServerMasterGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DisplayGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
#[serde(rename = "@Texture")]
pub texture: String,
}
impl_any_geometry!(DisplayGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReferenceGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "@Geometry")]
pub geometry: Option<Name>,
#[serde(rename = "Break", skip_serializing_if = "Vec::is_empty", default)]
pub breaks: Vec<Break>,
}
impl AnyGeometry for ReferenceGeometry {
fn name(&self) -> Option<&Name> {
self.name.as_ref()
}
fn model_name(&self) -> Option<&Name> {
self.model.as_ref()
}
fn children(&self) -> &[Geometry] {
&[]
}
fn validate_custom(&self, parent_fixture_type: &FixtureType, result: &mut ValidationResult) {
if let (Some(geometry), None) = (&self.geometry, self.geometry(parent_fixture_type)) {
result.errors.push(ValidationError::new(
ValidationObject::Geometry,
self.name().map(Name::to_string),
ValidationErrorType::LinkNotFound(
ValidationObject::Geometry,
Node::new([geometry.clone()]),
),
));
}
}
}
impl ReferenceGeometry {
pub fn geometry<'s>(&self, parent_fixture_type: &'s FixtureType) -> Option<&'s Geometry> {
parent_fixture_type.root_geometry(self.geometry.as_ref()?)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Break {
#[serde(rename = "@DMXOffset", default = "default_break_dmx_offset")]
pub dmx_offset: DmxAddress,
#[serde(rename = "@DMXBreak", default = "default_break_dmx_break")]
pub dmx_break: u8,
}
fn default_break_dmx_offset() -> DmxAddress {
DmxAddress::from_absolute(1)
}
fn default_break_dmx_break() -> u8 {
1
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LaserGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
#[serde(flatten, default)]
pub color_type: ColorType,
#[serde(rename = "@OutputStrength")]
pub output_strength: f64,
#[serde(rename = "@Emitter", skip_serializing_if = "Option::is_none")]
pub emitter: Option<Node>,
#[serde(rename = "@BeamDiameter")]
pub beam_diameter: f64,
#[serde(rename = "@BeamDivergenceMin")]
pub beam_divergence_min: f64,
#[serde(rename = "@BeamDivergenceMax")]
pub beam_divergence_max: f64,
#[serde(rename = "@ScanAnglePan")]
pub scan_angle_pan: f64,
#[serde(rename = "@ScanAngleTilt")]
pub scan_angle_tilt: f64,
#[serde(rename = "@ScanSpeed")]
pub scan_speed: f64,
#[serde(rename = "Protocol", skip_serializing_if = "Vec::is_empty", default)]
pub protocols: Vec<LaserProtocol>,
}
impl AnyGeometry for LaserGeometry {
fn name(&self) -> Option<&Name> {
self.name.as_ref()
}
fn model_name(&self) -> Option<&Name> {
self.model.as_ref()
}
fn children(&self) -> &[Geometry] {
&self.children
}
fn validate_custom(&self, parent_fixture_type: &FixtureType, result: &mut ValidationResult) {
if let (Some(emitter), None) = (&self.emitter, self.emitter(parent_fixture_type)) {
result.errors.push(ValidationError::new(
ValidationObject::Geometry,
self.name().map(Name::to_string),
ValidationErrorType::LinkNotFound(ValidationObject::Emitter, emitter.clone()),
));
}
let duplicate_protocol_name = self
.protocols
.iter()
.filter_map(|protocol| protocol.name.as_ref())
.find_duplicate();
if let Some(name) = duplicate_protocol_name {
result.errors.push(ValidationError::new(
ValidationObject::LaserProtocol,
name.to_string(),
ValidationErrorType::DuplicateName,
));
}
for protocol in &self.protocols {
protocol.validate(result);
}
}
}
impl LaserGeometry {
pub fn emitter<'s>(&self, parent_fixture_type: &'s FixtureType) -> Option<&'s Emitter> {
let name = self.emitter.as_ref()?.single()?;
parent_fixture_type.physical_descriptions.emitter(name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
#[serde(tag = "@ColorType")]
pub enum ColorType {
SingleWaveLength(
#[serde(rename = "@Color", deserialize_with = "Parse::deserialize")]
f64,
),
#[default]
#[serde(other, rename = "RGB")]
Rgb,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WiringObjectGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
#[serde(rename = "@ConnectorType")]
pub connector_type: String,
#[serde(flatten)]
pub component_type: ComponentType,
#[serde(rename = "@SignalType")]
pub signal_type: String,
#[serde(rename = "@PinCount")]
pub pin_count: i32,
#[serde(rename = "@SignalLayer")]
pub signal_layer: i32,
#[serde(rename = "@Orientation")]
pub orientation: Orientation,
#[serde(rename = "@WireGroup")]
pub wire_group: String,
#[serde(rename = "PinPatch", skip_serializing_if = "Vec::is_empty", default)]
pub pin_patches: Vec<PinPatch>,
}
impl AnyGeometry for WiringObjectGeometry {
fn name(&self) -> Option<&Name> {
self.name.as_ref()
}
fn model_name(&self) -> Option<&Name> {
self.model.as_ref()
}
fn children(&self) -> &[Geometry] {
&self.children
}
fn validate_custom(&self, parent_fixture_type: &FixtureType, result: &mut ValidationResult) {
for pin_patch in &self.pin_patches {
pin_patch.validate(parent_fixture_type, result);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(tag = "@ComponentType")]
pub enum ComponentType {
Input,
Output,
PowerSource {
#[serde(
rename = "@MaxPayLoad",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
max_pay_load: Option<f64>,
#[serde(
rename = "@Voltage",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
voltage: Option<f64>,
},
Consumer {
#[serde(
rename = "@ElectricalPayLoad",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
electrical_pay_load: Option<f64>,
#[serde(
rename = "@VoltageRangeMax",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
voltage_range_max: Option<f64>,
#[serde(
rename = "@VoltageRangeMin",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
voltage_range_min: Option<f64>,
#[serde(
rename = "@FrequencyRangeMax",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
frequency_range_max: Option<f64>,
#[serde(
rename = "@FrequencyRangeMin",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
frequency_range_min: Option<f64>,
#[serde(
rename = "@CosPhi",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
cos_phi: Option<f64>,
},
Fuse {
#[serde(
rename = "@FuseCurrent",
skip_serializing_if = "Option::is_none",
deserialize_with = "Parse::deserialize"
)]
fuse_current: Option<f64>,
#[serde(rename = "@FuseRating", skip_serializing_if = "Option::is_none")]
fuse_rating: Option<FuseRating>,
},
NetworkProvider,
NetworkInput,
NetworkOutput,
NetworkInOut,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum FuseRating {
B,
C,
D,
K,
Z,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Orientation {
Left,
Right,
Top,
Bottom,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PinPatch {
#[serde(rename = "@ToWiringObject")]
to_wiring_object: Node,
#[serde(rename = "@FromPin")]
from_pin: i32,
#[serde(rename = "@ToPin")]
to_pin: i32,
}
impl PinPatch {
pub fn to_wiring_object<'s>(
&self,
parent_fixture_type: &'s FixtureType,
) -> Option<&'s Geometry> {
parent_fixture_type.geometry_node(&self.to_wiring_object)
}
pub fn validate(&self, parent_fixture_type: &FixtureType, result: &mut ValidationResult) {
if self.to_wiring_object(parent_fixture_type).is_none() {
result.errors.push(ValidationError::new(
ValidationObject::PinPatch,
None,
ValidationErrorType::LinkNotFound(
ValidationObject::Geometry,
self.to_wiring_object.clone(),
),
));
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InventoryGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
#[serde(rename = "@Count")]
pub count: i32,
}
impl_any_geometry!(InventoryGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StructureGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
#[serde(rename = "@LinkedGeometry")]
pub linked_geometry: Name,
#[serde(rename = "@StructureType")]
pub structure_type: StructureType,
#[serde(flatten)]
pub cross_section_type: CrossSectionType,
}
impl_any_geometry!(StructureGeometry);
impl StructureGeometry {
pub fn linked_geometry<'s>(
&self,
parent_fixture_type: &'s FixtureType,
) -> Option<&'s Geometry> {
parent_fixture_type.nested_geometry(&self.linked_geometry)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum StructureType {
CenterLineBased,
Detail,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "@CrossSectionType")]
pub enum CrossSectionType {
TrussFramework {
#[serde(rename = "@TrussCrossSection")]
section: String,
},
Tube {
#[serde(
rename = "@CrossSectionHeight",
deserialize_with = "Parse::deserialize"
)]
height: f64,
#[serde(
rename = "@CrossSectionWallThickness",
deserialize_with = "Parse::deserialize"
)]
wall_thickness: f64,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SupportGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
#[serde(flatten)]
pub support_type: SupportType,
#[serde(rename = "@CapacityX")]
pub capacity_x: f64,
#[serde(rename = "@CapacityY")]
pub capacity_y: f64,
#[serde(rename = "@CapacityZ")]
pub capacity_z: f64,
#[serde(rename = "@CapacityXX")]
pub capacity_xx: f64,
#[serde(rename = "@CapacityYY")]
pub capacity_yy: f64,
#[serde(rename = "@CapacityZZ")]
pub capacity_zz: f64,
}
impl_any_geometry!(SupportGeometry);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "@SupportType")]
pub enum SupportType {
Rope {
#[serde(rename = "@RopeCrossSection")]
cross_section: String,
#[serde(rename = "@RopeOffset")]
offset: Vector3,
},
GroundSupport {
#[serde(rename = "@ResistanceX", deserialize_with = "Parse::deserialize")]
resistance_x: f64,
#[serde(rename = "@ResistanceY", deserialize_with = "Parse::deserialize")]
resistance_y: f64,
#[serde(rename = "@ResistanceZ", deserialize_with = "Parse::deserialize")]
resistance_z: f64,
#[serde(rename = "@ResistanceXX", deserialize_with = "Parse::deserialize")]
resistance_xx: f64,
#[serde(rename = "@ResistanceYY", deserialize_with = "Parse::deserialize")]
resistance_yy: f64,
#[serde(rename = "@ResistanceZZ", deserialize_with = "Parse::deserialize")]
resistance_zz: f64,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MagnetGeometry {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Model", skip_serializing_if = "Option::is_none")]
pub model: Option<Name>,
#[serde(rename = "@Position")]
pub position: Matrix,
#[serde(rename = "$value", skip_serializing_if = "Vec::is_empty", default)]
pub children: Vec<Geometry>,
}
impl_any_geometry!(MagnetGeometry);
fn default_power_consumption() -> f64 {
1000.
}
fn default_luminous_flux() -> f64 {
10000.
}
fn default_beam_angle() -> f64 {
25.
}
fn default_field_angle() -> f64 {
25.
}
fn default_throw_ratio() -> f64 {
1.
}
fn default_rectangle_ratio() -> f64 {
1.7777
}
fn default_beam_radius() -> f64 {
0.05
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct LaserProtocol {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
}
impl LaserProtocol {
pub fn validate(&self, result: &mut ValidationResult) {
if self.name.is_none() {
result.errors.push(ValidationError::new(
ValidationObject::LaserProtocol,
None,
ValidationErrorType::MissingName,
));
}
}
}