1use crate::adjacency::NodeAdjacency;
21use crate::geom::{element_axis as elem_axis, vec3, ORTHOGONAL_DOT_MAX};
22use crate::model::{ElementKind, Model};
23
24fn face_at(
27 model: &Model,
28 node: crate::ids::NodeId,
29 target_axis: [f64; 3],
30 target_elem_idx: usize,
31 adjacency: &NodeAdjacency,
32) -> f64 {
33 let mut d_max = 0.0_f64;
34 for &ei in adjacency.indices_at(node) {
35 if ei == target_elem_idx {
36 continue;
37 }
38 let e = &model.elements[ei];
39 if e.kind != ElementKind::Beam {
40 continue;
41 }
42 let axis = elem_axis(model, e);
43 if vec3::dot(axis, target_axis).abs() >= ORTHOGONAL_DOT_MAX {
44 continue;
45 }
46 if let Some(sec) = e.section.and_then(|sid| model.sections.get(sid.index())) {
47 d_max = d_max.max(sec.depth);
48 }
49 }
50 d_max / 2.0
51}
52
53pub fn face_distances(model: &Model) -> Vec<[f64; 2]> {
60 let adjacency = NodeAdjacency::build(model);
61 model
62 .elements
63 .iter()
64 .enumerate()
65 .map(|(i, e)| {
66 if e.kind != ElementKind::Beam || e.nodes.len() < 2 {
67 return [0.0, 0.0];
68 }
69 let axis = elem_axis(model, e);
70 let ni = e.nodes[0];
71 let nj = e.nodes[e.nodes.len() - 1];
72 [
73 face_at(model, ni, axis, i, &adjacency),
74 face_at(model, nj, axis, i, &adjacency),
75 ]
76 })
77 .collect()
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use crate::ids::{ElemId, MaterialId, NodeId, SectionId};
84 use crate::model::{
85 ElementData, EndCondition, ForceRegime, LocalAxis, Node, RigidZone, Section,
86 };
87
88 fn node(id: u32, c: [f64; 3]) -> Node {
89 Node {
90 id: NodeId(id),
91 coord: c,
92 restraint: Default::default(),
93 mass: None,
94 story: None,
95 support_spring: None,
96 }
97 }
98
99 fn section(id: u32, depth: f64) -> Section {
100 Section {
101 id: SectionId(id),
102 name: String::new(),
103 area: 0.0,
104 iy: 0.0,
105 iz: 0.0,
106 j: 0.0,
107 depth,
108 width: 0.0,
109 as_y: 0.0,
110 as_z: 0.0,
111 floor: None,
112 panel_thickness: None,
113 thickness: None,
114 shape: None,
115 material: Some(MaterialId(0)),
116 rebar_material: None,
117 shear_rebar_material: None,
118 steel_material: None,
119 }
120 }
121
122 fn elem(id: u32, kind: ElementKind, a: u32, b: u32, sec: u32) -> ElementData {
123 ElementData {
124 id: ElemId(id),
125 kind,
126 nodes: [NodeId(a), NodeId(b)].into_iter().collect(),
127 section: Some(SectionId(sec)),
128 local_axis: LocalAxis {
129 ref_vector: [0.0, 0.0, 1.0],
130 },
131 end_cond: [EndCondition::Fixed, EndCondition::Fixed],
132 force_regime: ForceRegime::Auto,
133 rigid_zone: RigidZone::default(),
134 plastic_zone: None,
135 spring: None,
136 }
137 }
138
139 #[test]
141 fn 直交材のせいの半分をフェース距離とする() {
142 let model = Model {
143 nodes: vec![
144 node(0, [0.0, 0.0, 0.0]),
145 node(1, [0.0, 0.0, 3000.0]),
146 node(2, [4000.0, 0.0, 3000.0]),
147 ],
148 elements: vec![
149 elem(0, ElementKind::Beam, 0, 1, 0),
150 elem(1, ElementKind::Beam, 1, 2, 1),
151 ],
152 sections: vec![section(0, 600.0), section(1, 700.0)],
153 ..Default::default()
154 };
155 let f = face_distances(&model);
156 assert_eq!(f[1], [300.0, 0.0]);
158 assert_eq!(f[0], [0.0, 350.0]);
160 }
161
162 #[test]
166 fn 壁はフェース距離に数えない() {
167 let mut model = Model {
168 nodes: vec![
169 node(0, [0.0, 0.0, 0.0]),
170 node(1, [0.0, 0.0, 3000.0]),
171 node(2, [4000.0, 0.0, 3000.0]),
172 ],
173 elements: vec![elem(0, ElementKind::Beam, 1, 2, 0)],
174 sections: vec![section(0, 700.0), section(1, 9999.0)],
175 ..Default::default()
176 };
177 assert_eq!(face_distances(&model)[0], [0.0, 0.0]);
178
179 model.elements.push(elem(1, ElementKind::Wall, 0, 1, 1));
181 assert_eq!(face_distances(&model)[0], [0.0, 0.0]);
182
183 model.elements.push(elem(2, ElementKind::Beam, 0, 1, 1));
185 assert_eq!(face_distances(&model)[0], [4999.5, 0.0]);
186 }
187}