use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct SymbolicPolyline {
express_id: u32,
ifc_type: String,
points: Vec<f32>,
is_closed: bool,
world_y: f32,
rep_identifier: String,
}
#[wasm_bindgen]
impl SymbolicPolyline {
#[wasm_bindgen(getter, js_name = expressId)]
pub fn express_id(&self) -> u32 {
self.express_id
}
#[wasm_bindgen(getter, js_name = ifcType)]
pub fn ifc_type(&self) -> String {
self.ifc_type.clone()
}
#[wasm_bindgen(getter)]
pub fn points(&self) -> js_sys::Float32Array {
js_sys::Float32Array::from(&self.points[..])
}
#[wasm_bindgen(getter, js_name = pointCount)]
pub fn point_count(&self) -> usize {
self.points.len() / 2
}
#[wasm_bindgen(getter, js_name = isClosed)]
pub fn is_closed(&self) -> bool {
self.is_closed
}
#[wasm_bindgen(getter, js_name = repIdentifier)]
pub fn rep_identifier(&self) -> String {
self.rep_identifier.clone()
}
#[wasm_bindgen(getter, js_name = worldY)]
pub fn world_y(&self) -> f32 {
self.world_y
}
}
impl SymbolicPolyline {
#[allow(clippy::too_many_arguments)]
pub fn new(
express_id: u32,
ifc_type: String,
points: Vec<f32>,
is_closed: bool,
world_y: f32,
rep_identifier: String,
) -> Self {
Self {
express_id,
ifc_type,
points,
is_closed,
world_y,
rep_identifier,
}
}
}
#[wasm_bindgen]
pub struct SymbolicCircle {
express_id: u32,
ifc_type: String,
center_x: f32,
center_y: f32,
radius: f32,
world_y: f32,
start_angle: f32,
end_angle: f32,
rep_identifier: String,
}
#[wasm_bindgen]
impl SymbolicCircle {
#[wasm_bindgen(getter, js_name = expressId)]
pub fn express_id(&self) -> u32 {
self.express_id
}
#[wasm_bindgen(getter, js_name = ifcType)]
pub fn ifc_type(&self) -> String {
self.ifc_type.clone()
}
#[wasm_bindgen(getter, js_name = centerX)]
pub fn center_x(&self) -> f32 {
self.center_x
}
#[wasm_bindgen(getter, js_name = centerY)]
pub fn center_y(&self) -> f32 {
self.center_y
}
#[wasm_bindgen(getter)]
pub fn radius(&self) -> f32 {
self.radius
}
#[wasm_bindgen(getter, js_name = startAngle)]
pub fn start_angle(&self) -> f32 {
self.start_angle
}
#[wasm_bindgen(getter, js_name = endAngle)]
pub fn end_angle(&self) -> f32 {
self.end_angle
}
#[wasm_bindgen(getter, js_name = repIdentifier)]
pub fn rep_identifier(&self) -> String {
self.rep_identifier.clone()
}
#[wasm_bindgen(getter, js_name = isFullCircle)]
pub fn is_full_circle(&self) -> bool {
(self.end_angle - self.start_angle - std::f32::consts::TAU).abs() < 0.001
}
#[wasm_bindgen(getter, js_name = worldY)]
pub fn world_y(&self) -> f32 {
self.world_y
}
}
impl SymbolicCircle {
#[allow(clippy::too_many_arguments)]
pub fn new(
express_id: u32,
ifc_type: String,
center_x: f32,
center_y: f32,
radius: f32,
world_y: f32,
start_angle: f32,
end_angle: f32,
rep_identifier: String,
) -> Self {
Self {
express_id,
ifc_type,
center_x,
center_y,
radius,
world_y,
start_angle,
end_angle,
rep_identifier,
}
}
pub fn full_circle(
express_id: u32,
ifc_type: String,
center_x: f32,
center_y: f32,
radius: f32,
world_y: f32,
rep_identifier: String,
) -> Self {
Self::new(
express_id,
ifc_type,
center_x,
center_y,
radius,
world_y,
0.0,
std::f32::consts::TAU,
rep_identifier,
)
}
}
#[wasm_bindgen]
pub struct SymbolicText {
express_id: u32,
ifc_type: String,
x: f32,
y: f32,
dir_x: f32,
dir_y: f32,
height: f32,
content: String,
alignment: String,
world_y: f32,
color_r: f32,
color_g: f32,
color_b: f32,
color_a: f32,
target_px: f32,
rep_identifier: String,
}
#[wasm_bindgen]
impl SymbolicText {
#[wasm_bindgen(getter, js_name = expressId)]
pub fn express_id(&self) -> u32 { self.express_id }
#[wasm_bindgen(getter, js_name = ifcType)]
pub fn ifc_type(&self) -> String { self.ifc_type.clone() }
#[wasm_bindgen(getter)]
pub fn x(&self) -> f32 { self.x }
#[wasm_bindgen(getter)]
pub fn y(&self) -> f32 { self.y }
#[wasm_bindgen(getter, js_name = dirX)]
pub fn dir_x(&self) -> f32 { self.dir_x }
#[wasm_bindgen(getter, js_name = dirY)]
pub fn dir_y(&self) -> f32 { self.dir_y }
#[wasm_bindgen(getter)]
pub fn height(&self) -> f32 { self.height }
#[wasm_bindgen(getter)]
pub fn content(&self) -> String { self.content.clone() }
#[wasm_bindgen(getter)]
pub fn alignment(&self) -> String { self.alignment.clone() }
#[wasm_bindgen(getter, js_name = worldY)]
pub fn world_y(&self) -> f32 { self.world_y }
#[wasm_bindgen(getter, js_name = colorR)]
pub fn color_r(&self) -> f32 { self.color_r }
#[wasm_bindgen(getter, js_name = colorG)]
pub fn color_g(&self) -> f32 { self.color_g }
#[wasm_bindgen(getter, js_name = colorB)]
pub fn color_b(&self) -> f32 { self.color_b }
#[wasm_bindgen(getter, js_name = colorA)]
pub fn color_a(&self) -> f32 { self.color_a }
#[wasm_bindgen(getter, js_name = targetPx)]
pub fn target_px(&self) -> f32 { self.target_px }
#[wasm_bindgen(getter, js_name = repIdentifier)]
pub fn rep_identifier(&self) -> String { self.rep_identifier.clone() }
}
impl SymbolicText {
#[allow(clippy::too_many_arguments)]
pub fn new(
express_id: u32,
ifc_type: String,
x: f32,
y: f32,
dir_x: f32,
dir_y: f32,
height: f32,
content: String,
alignment: String,
world_y: f32,
rep_identifier: String,
) -> Self {
Self::new_styled(
express_id, ifc_type, x, y, dir_x, dir_y,
height, content, alignment, world_y,
[0.05, 0.05, 0.05, 1.0], 0.0, rep_identifier,
)
}
#[allow(clippy::too_many_arguments)]
pub fn new_styled(
express_id: u32,
ifc_type: String,
x: f32,
y: f32,
dir_x: f32,
dir_y: f32,
height: f32,
content: String,
alignment: String,
world_y: f32,
rgba: [f32; 4],
target_px: f32,
rep_identifier: String,
) -> Self {
Self {
express_id,
ifc_type,
x,
y,
dir_x,
dir_y,
height,
content,
alignment,
world_y,
color_r: rgba[0],
color_g: rgba[1],
color_b: rgba[2],
color_a: rgba[3],
target_px,
rep_identifier,
}
}
}
#[wasm_bindgen]
pub struct SymbolicFillArea {
express_id: u32,
ifc_type: String,
points: Vec<f32>,
holes_offsets: Vec<u32>,
fill_r: f32,
fill_g: f32,
fill_b: f32,
fill_a: f32,
has_hatching: bool,
hatch_spacing: f32,
hatch_angle: f32,
hatch_angle_secondary: f32,
hatch_line_width: f32,
world_y: f32,
rep_identifier: String,
}
#[wasm_bindgen]
impl SymbolicFillArea {
#[wasm_bindgen(getter, js_name = expressId)]
pub fn express_id(&self) -> u32 { self.express_id }
#[wasm_bindgen(getter, js_name = ifcType)]
pub fn ifc_type(&self) -> String { self.ifc_type.clone() }
#[wasm_bindgen(getter)]
pub fn points(&self) -> js_sys::Float32Array {
js_sys::Float32Array::from(&self.points[..])
}
#[wasm_bindgen(getter, js_name = pointCount)]
pub fn point_count(&self) -> usize { self.points.len() / 2 }
#[wasm_bindgen(getter, js_name = holesOffsets)]
pub fn holes_offsets(&self) -> js_sys::Uint32Array {
js_sys::Uint32Array::from(&self.holes_offsets[..])
}
#[wasm_bindgen(getter, js_name = holeCount)]
pub fn hole_count(&self) -> usize { self.holes_offsets.len() }
#[wasm_bindgen(getter, js_name = fillR)]
pub fn fill_r(&self) -> f32 { self.fill_r }
#[wasm_bindgen(getter, js_name = fillG)]
pub fn fill_g(&self) -> f32 { self.fill_g }
#[wasm_bindgen(getter, js_name = fillB)]
pub fn fill_b(&self) -> f32 { self.fill_b }
#[wasm_bindgen(getter, js_name = fillA)]
pub fn fill_a(&self) -> f32 { self.fill_a }
#[wasm_bindgen(getter, js_name = hasHatching)]
pub fn has_hatching(&self) -> bool { self.has_hatching }
#[wasm_bindgen(getter, js_name = hatchSpacing)]
pub fn hatch_spacing(&self) -> f32 { self.hatch_spacing }
#[wasm_bindgen(getter, js_name = hatchAngle)]
pub fn hatch_angle(&self) -> f32 { self.hatch_angle }
#[wasm_bindgen(getter, js_name = hatchAngleSecondary)]
pub fn hatch_angle_secondary(&self) -> f32 { self.hatch_angle_secondary }
#[wasm_bindgen(getter, js_name = hatchLineWidth)]
pub fn hatch_line_width(&self) -> f32 { self.hatch_line_width }
#[wasm_bindgen(getter, js_name = worldY)]
pub fn world_y(&self) -> f32 { self.world_y }
#[wasm_bindgen(getter, js_name = repIdentifier)]
pub fn rep_identifier(&self) -> String { self.rep_identifier.clone() }
}
impl SymbolicFillArea {
#[allow(clippy::too_many_arguments)]
pub fn new(
express_id: u32,
ifc_type: String,
points: Vec<f32>,
holes_offsets: Vec<u32>,
fill_rgba: [f32; 4],
world_y: f32,
rep_identifier: String,
) -> Self {
Self {
express_id,
ifc_type,
points,
holes_offsets,
fill_r: fill_rgba[0],
fill_g: fill_rgba[1],
fill_b: fill_rgba[2],
fill_a: fill_rgba[3],
has_hatching: false,
hatch_spacing: 0.0,
hatch_angle: 0.0,
hatch_angle_secondary: f32::NAN,
hatch_line_width: 0.0,
world_y,
rep_identifier,
}
}
pub fn with_hatching(
mut self,
spacing: f32,
angle: f32,
angle_secondary: Option<f32>,
line_width: f32,
) -> Self {
self.has_hatching = true;
self.hatch_spacing = spacing;
self.hatch_angle = angle;
self.hatch_angle_secondary = angle_secondary.unwrap_or(f32::NAN);
self.hatch_line_width = line_width;
self
}
}
#[wasm_bindgen]
pub struct SymbolicRepresentationCollection {
polylines: Vec<SymbolicPolyline>,
circles: Vec<SymbolicCircle>,
texts: Vec<SymbolicText>,
fills: Vec<SymbolicFillArea>,
}
#[wasm_bindgen]
impl SymbolicRepresentationCollection {
#[wasm_bindgen(js_name = getExpressIds)]
pub fn get_express_ids(&self) -> Vec<u32> {
let mut ids: Vec<u32> = self
.polylines
.iter()
.map(|p| p.express_id)
.chain(self.circles.iter().map(|c| c.express_id))
.chain(self.texts.iter().map(|t| t.express_id))
.chain(self.fills.iter().map(|f| f.express_id))
.collect();
ids.sort_unstable();
ids.dedup();
ids
}
#[wasm_bindgen(getter, js_name = polylineCount)]
pub fn polyline_count(&self) -> usize {
self.polylines.len()
}
#[wasm_bindgen(getter, js_name = circleCount)]
pub fn circle_count(&self) -> usize {
self.circles.len()
}
#[wasm_bindgen(getter, js_name = textCount)]
pub fn text_count(&self) -> usize { self.texts.len() }
#[wasm_bindgen(getter, js_name = fillCount)]
pub fn fill_count(&self) -> usize { self.fills.len() }
#[wasm_bindgen(getter, js_name = totalCount)]
pub fn total_count(&self) -> usize {
self.polylines.len() + self.circles.len() + self.texts.len() + self.fills.len()
}
#[wasm_bindgen(getter, js_name = isEmpty)]
pub fn is_empty(&self) -> bool {
self.polylines.is_empty()
&& self.circles.is_empty()
&& self.texts.is_empty()
&& self.fills.is_empty()
}
#[wasm_bindgen(js_name = getPolyline)]
pub fn get_polyline(&self, index: usize) -> Option<SymbolicPolyline> {
self.polylines.get(index).map(|p| SymbolicPolyline {
express_id: p.express_id,
ifc_type: p.ifc_type.clone(),
points: p.points.clone(),
is_closed: p.is_closed,
world_y: p.world_y,
rep_identifier: p.rep_identifier.clone(),
})
}
#[wasm_bindgen(js_name = getCircle)]
pub fn get_circle(&self, index: usize) -> Option<SymbolicCircle> {
self.circles.get(index).map(|c| SymbolicCircle {
express_id: c.express_id,
ifc_type: c.ifc_type.clone(),
center_x: c.center_x,
center_y: c.center_y,
radius: c.radius,
world_y: c.world_y,
start_angle: c.start_angle,
end_angle: c.end_angle,
rep_identifier: c.rep_identifier.clone(),
})
}
#[wasm_bindgen(js_name = getText)]
pub fn get_text(&self, index: usize) -> Option<SymbolicText> {
self.texts.get(index).map(|t| SymbolicText {
express_id: t.express_id,
ifc_type: t.ifc_type.clone(),
x: t.x,
y: t.y,
dir_x: t.dir_x,
dir_y: t.dir_y,
height: t.height,
content: t.content.clone(),
alignment: t.alignment.clone(),
world_y: t.world_y,
color_r: t.color_r,
color_g: t.color_g,
color_b: t.color_b,
color_a: t.color_a,
target_px: t.target_px,
rep_identifier: t.rep_identifier.clone(),
})
}
#[wasm_bindgen(js_name = getFill)]
pub fn get_fill(&self, index: usize) -> Option<SymbolicFillArea> {
self.fills.get(index).map(|f| SymbolicFillArea {
express_id: f.express_id,
ifc_type: f.ifc_type.clone(),
points: f.points.clone(),
holes_offsets: f.holes_offsets.clone(),
fill_r: f.fill_r,
fill_g: f.fill_g,
fill_b: f.fill_b,
fill_a: f.fill_a,
has_hatching: f.has_hatching,
hatch_spacing: f.hatch_spacing,
hatch_angle: f.hatch_angle,
hatch_angle_secondary: f.hatch_angle_secondary,
hatch_line_width: f.hatch_line_width,
world_y: f.world_y,
rep_identifier: f.rep_identifier.clone(),
})
}
}
impl SymbolicRepresentationCollection {
pub fn new() -> Self {
Self {
polylines: Vec::new(),
circles: Vec::new(),
texts: Vec::new(),
fills: Vec::new(),
}
}
pub fn with_capacity(polyline_capacity: usize, circle_capacity: usize) -> Self {
Self {
polylines: Vec::with_capacity(polyline_capacity),
circles: Vec::with_capacity(circle_capacity),
texts: Vec::new(),
fills: Vec::new(),
}
}
pub fn add_polyline(&mut self, polyline: SymbolicPolyline) {
self.polylines.push(polyline);
}
pub fn add_circle(&mut self, circle: SymbolicCircle) {
self.circles.push(circle);
}
pub fn add_text(&mut self, text: SymbolicText) {
self.texts.push(text);
}
pub fn add_fill(&mut self, fill: SymbolicFillArea) {
self.fills.push(fill);
}
pub fn from_data(data: ifc_lite_processing::SymbolicData) -> Self {
let mut collection = Self::with_capacity(data.polylines.len(), data.circles.len());
for p in data.polylines {
collection.add_polyline(SymbolicPolyline::new(
p.express_id,
p.ifc_type,
p.points,
p.closed,
p.world_y,
p.representation,
));
}
for c in data.circles {
collection.add_circle(SymbolicCircle::new(
c.express_id,
c.ifc_type,
c.center_x,
c.center_y,
c.radius,
c.world_y,
c.start_angle,
c.end_angle,
c.representation,
));
}
for t in data.texts {
collection.add_text(SymbolicText::new_styled(
t.express_id,
t.ifc_type,
t.x,
t.y,
t.dir_x,
t.dir_y,
t.height,
t.content,
t.alignment,
t.world_y,
t.color,
t.target_px,
t.representation,
));
}
for f in data.fills {
collection.add_fill(SymbolicFillArea::new(
f.express_id,
f.ifc_type,
f.points,
f.holes_offsets,
f.fill_color,
f.world_y,
f.representation,
));
}
collection
}
}
impl Default for SymbolicRepresentationCollection {
fn default() -> Self {
Self::new()
}
}