use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::error::GeometryError;
use crate::resource::topology::slot;
use super::{invalid, refs};
pub fn vertex_point(tx: &mut Transaction, geometry: EntityId) -> EntityId {
let mut attrs = vec![Value::Null; 1];
attrs[slot::VERTEX_GEOMETRY] = Value::Ref(geometry);
tx.create(Entity::new("IFCVERTEXPOINT", attrs))
}
pub fn edge(tx: &mut Transaction, start: EntityId, end: EntityId) -> EntityId {
let mut attrs = vec![Value::Null; 2];
attrs[slot::EDGE_START] = Value::Ref(start);
attrs[slot::EDGE_END] = Value::Ref(end);
tx.create(Entity::new("IFCEDGE", attrs))
}
pub fn edge_curve(
tx: &mut Transaction,
start: EntityId,
end: EntityId,
geometry: EntityId,
same_sense: bool,
) -> EntityId {
let mut attrs = vec![Value::Null; 4];
attrs[slot::EDGE_START] = Value::Ref(start);
attrs[slot::EDGE_END] = Value::Ref(end);
attrs[slot::EDGE_GEOMETRY] = Value::Ref(geometry);
attrs[slot::EDGE_SAME_SENSE] = Value::Bool(same_sense);
tx.create(Entity::new("IFCEDGECURVE", attrs))
}
pub fn oriented_edge(tx: &mut Transaction, element: EntityId, orientation: bool) -> EntityId {
let mut attrs = vec![Value::Null; 4];
attrs[slot::EDGE_START] = Value::Derived;
attrs[slot::EDGE_END] = Value::Derived;
attrs[slot::EDGE_ELEMENT] = Value::Ref(element);
attrs[slot::EDGE_ORIENTATION] = Value::Bool(orientation);
tx.create(Entity::new("IFCORIENTEDEDGE", attrs))
}
pub fn subedge(tx: &mut Transaction, start: EntityId, end: EntityId, parent: EntityId) -> EntityId {
let mut attrs = vec![Value::Null; 3];
attrs[slot::EDGE_START] = Value::Ref(start);
attrs[slot::EDGE_END] = Value::Ref(end);
attrs[slot::PARENT_EDGE] = Value::Ref(parent);
tx.create(Entity::new("IFCSUBEDGE", attrs))
}
pub fn poly_loop(tx: &mut Transaction, polygon: &[EntityId]) -> Result<EntityId, GeometryError> {
const T: &str = "IFCPOLYLOOP";
if polygon.len() < 3 {
return Err(invalid(
T,
"Polygon",
format!("expected at least 3 points, got {}", polygon.len()),
));
}
for (i, point) in polygon.iter().enumerate() {
if polygon[..i].contains(point) {
return Err(invalid(
T,
"Polygon",
"the point list is UNIQUE; a loop closes implicitly, so the \
first point must not be repeated at the end",
));
}
}
let mut attrs = vec![Value::Null; 1];
attrs[slot::POLYGON] = refs(polygon);
Ok(tx.create(Entity::new(T, attrs)))
}
pub fn edge_loop(tx: &mut Transaction, edges: &[EntityId]) -> Result<EntityId, GeometryError> {
const T: &str = "IFCEDGELOOP";
if edges.is_empty() {
return Err(invalid(T, "EdgeList", "expected at least one edge"));
}
let mut attrs = vec![Value::Null; 1];
attrs[slot::EDGE_LIST] = refs(edges);
Ok(tx.create(Entity::new(T, attrs)))
}
pub fn vertex_loop(tx: &mut Transaction, vertex: EntityId) -> EntityId {
tx.create(Entity::new("IFCVERTEXLOOP", vec![Value::Ref(vertex)]))
}
pub fn face_bound(tx: &mut Transaction, bound: EntityId, orientation: bool) -> EntityId {
bound_entity(tx, "IFCFACEBOUND", bound, orientation)
}
pub fn face_outer_bound(tx: &mut Transaction, bound: EntityId, orientation: bool) -> EntityId {
bound_entity(tx, "IFCFACEOUTERBOUND", bound, orientation)
}
fn bound_entity(
tx: &mut Transaction,
type_name: &'static str,
bound: EntityId,
orientation: bool,
) -> EntityId {
let mut attrs = vec![Value::Null; 2];
attrs[slot::BOUND] = Value::Ref(bound);
attrs[slot::ORIENTATION] = Value::Bool(orientation);
tx.create(Entity::new(type_name, attrs))
}
pub fn face(tx: &mut Transaction, bounds: &[EntityId]) -> Result<EntityId, GeometryError> {
const T: &str = "IFCFACE";
if bounds.is_empty() {
return Err(invalid(T, "Bounds", "expected at least one bound"));
}
let mut attrs = vec![Value::Null; 1];
attrs[slot::BOUNDS] = refs(bounds);
Ok(tx.create(Entity::new(T, attrs)))
}
pub fn face_surface(
tx: &mut Transaction,
bounds: &[EntityId],
surface: EntityId,
same_sense: bool,
advanced: bool,
) -> Result<EntityId, GeometryError> {
let type_name = if advanced {
"IFCADVANCEDFACE"
} else {
"IFCFACESURFACE"
};
if bounds.is_empty() {
return Err(invalid(type_name, "Bounds", "expected at least one bound"));
}
let mut attrs = vec![Value::Null; 3];
attrs[slot::BOUNDS] = refs(bounds);
attrs[slot::FACE_SURFACE] = Value::Ref(surface);
attrs[slot::FACE_SAME_SENSE] = Value::Bool(same_sense);
Ok(tx.create(Entity::new(type_name, attrs)))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShellKind {
Closed,
Open,
Connected,
}
impl ShellKind {
fn type_name(self) -> &'static str {
match self {
Self::Closed => "IFCCLOSEDSHELL",
Self::Open => "IFCOPENSHELL",
Self::Connected => "IFCCONNECTEDFACESET",
}
}
}
pub fn shell(
tx: &mut Transaction,
kind: ShellKind,
faces: &[EntityId],
) -> Result<EntityId, GeometryError> {
let type_name = kind.type_name();
if faces.is_empty() {
return Err(invalid(type_name, "CfsFaces", "expected at least one face"));
}
let mut attrs = vec![Value::Null; 1];
attrs[slot::CFS_FACES] = refs(faces);
Ok(tx.create(Entity::new(type_name, attrs)))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrepKind {
Faceted,
Advanced,
}
pub fn manifold_solid_brep(
tx: &mut Transaction,
kind: BrepKind,
outer: EntityId,
voids: &[EntityId],
) -> EntityId {
let type_name = match (kind, voids.is_empty()) {
(BrepKind::Faceted, true) => "IFCFACETEDBREP",
(BrepKind::Faceted, false) => "IFCFACETEDBREPWITHVOIDS",
(BrepKind::Advanced, true) => "IFCADVANCEDBREP",
(BrepKind::Advanced, false) => "IFCADVANCEDBREPWITHVOIDS",
};
let mut attrs = vec![Value::Null; if voids.is_empty() { 1 } else { 2 }];
attrs[slot::OUTER] = Value::Ref(outer);
if !voids.is_empty() {
attrs[slot::VOIDS] = refs(voids);
}
tx.create(Entity::new(type_name, attrs))
}
pub fn shell_based_surface_model(
tx: &mut Transaction,
shells: &[EntityId],
) -> Result<EntityId, GeometryError> {
const T: &str = "IFCSHELLBASEDSURFACEMODEL";
if shells.is_empty() {
return Err(invalid(T, "SbsmBoundary", "expected at least one shell"));
}
Ok(tx.create(Entity::new(T, vec![refs(shells)])))
}
pub fn face_based_surface_model(
tx: &mut Transaction,
face_sets: &[EntityId],
) -> Result<EntityId, GeometryError> {
const T: &str = "IFCFACEBASEDSURFACEMODEL";
if face_sets.is_empty() {
return Err(invalid(T, "FbsmFaces", "expected at least one face set"));
}
Ok(tx.create(Entity::new(T, vec![refs(face_sets)])))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BareTopology {
Loop,
Vertex,
}
pub fn bare_topology(tx: &mut Transaction, kind: BareTopology) -> EntityId {
let entity = match kind {
BareTopology::Loop => "IFCLOOP",
BareTopology::Vertex => "IFCVERTEX",
};
tx.create(Entity::new(entity, Vec::new()))
}