import oc from "../opencascade/initializer";
import explore from "../utils/explore";
import cachedGetters from "../utils/cache";
import {Face, Edge, Vertex} from "./index";
import {Axis, Plane, Vector} from "../math";
import {cut, intersect, join} from "../operations/booleans";
import {translate, rotate, mirror, scale} from "../operations/transformations";
import {split} from "../operations/modifications";
import {DEFAULT_PRECISION, JOIN_TYPES} from "../constants";
/**
* Represents a solid in 3D space.
* @memberof modeling
* @alias Solid
*/
export class Solid {
#wrapped;
#properties;
/**
* @hideconstructor
*/
constructor(wrapped) {
this.#wrapped = oc.TopoDS.Solid_1(wrapped);
}
/**
* Returns the wrapped OpenCascade object.
* @private
*/
get wrapped() {
return this.#wrapped;
}
/**
* Retrieves all faces of the solid.
* @returns {Face[]} An array of `Face` objects representing the solid's faces.
*/
get faces() {
return explore({shape: this.#wrapped, find: oc.TopAbs_ShapeEnum.TopAbs_FACE}).map((shape) => new Face(shape));
}
/**
* Retrieves all edges of the solid.
* @returns {Edge[]} An array of `Edge` objects representing the solid's edges.
*/
get edges() {
return explore({shape: this.#wrapped, find: oc.TopAbs_ShapeEnum.TopAbs_EDGE}).map((shape) => new Edge(shape));
}
/**
* Retrieves all vertices of the solid.
* @returns {Vertex[]} An array of `Vertex` objects representing the solid's vertices.
*/
get vertices() {
return explore({shape: this.#wrapped, find: oc.TopAbs_ShapeEnum.TopAbs_VERTEX}).map((shape) => new Vertex(shape));
}
/**
* Computes the center of mass of the solid.
* @returns {Vector} A `Vector` object representing the center of mass of the solid.
*/
get centerOfMass() {
const centerOfMass = this.#surfaceProperties().CentreOfMass();
return new Vector({
x: centerOfMass.X(),
y: centerOfMass.Y(),
z: centerOfMass.Z(),
});
}
/**
* Computes the volume of the solid.
* @returns {number} The volume of the solid.
*/
get volume() {
return this.#surfaceProperties().Mass();
}
/**
* Translates the solid by a specified offset.
* @param {Vector} offset - The translation offset.
* @returns {Solid} A new `Solid` object representing the translated solid.
*/
translate(offset) {
return new Solid(translate({shape: this.#wrapped, offset: offset.wrapped}));
}
/**
* Rotates the solid around a specified axis by a given angle.
* @param {Object} parameters - Rotation parameters.
* @param {Axis} parameters.axis - The axis to rotate around.
* @param {number} parameters.angle - The rotation angle in radians.
* @returns {Solid} A new `Solid` object representing the rotated solid.
*/
rotate({axis, angle}) {
return new Solid(rotate({shape: this.#wrapped, axis: axis.wrapped, angle: angle}));
}
/**
* Mirrors the solid across a specified plane.
* @param {Plane} plane - The plane to mirror across.
* @returns {Solid} A new `Solid` object representing the mirrored solid.
*/
mirror(plane) {
return new Solid(mirror({shape: this.#wrapped, axis: plane.wrapped.Position().Ax2()}));
}
/**
* Scales the solid by a specified factor.
* @param {number} factor - The scaling factor.
* @returns {Solid} A new `Solid` object representing the scaled solid.
*/
scale(factor) {
return new Solid(scale({shape: this.#wrapped, factor}));
}
/**
* Splits the solid into multiple parts using a specified tool.
* @param {Plane} tool - The tool used to split the solid.
* @returns {Solid[]} An array of `Solid` objects representing the split parts.
*/
split(tool) {
const face = new oc.BRepBuilderAPI_MakeFace_3(tool.wrapped).Face();
return explore({shape: split({shape: this.#wrapped, tool: face}), find: oc.TopAbs_ShapeEnum.TopAbs_SOLID}).map(
(shape) => new Solid(shape)
);
}
/**
* Cuts the solid using one or more tools (other solids).
* @param {...Solid} tools - One or more `Solid` objects to use as cutting tools.
* @returns {Solid[]} An array of `Solid` objects representing the result.
*/
cut(...tools) {
const compound = cut({
shape: this.#wrapped,
tools: tools.map((tool) => tool.wrapped),
});
return explore({shape: compound, find: oc.TopAbs_ShapeEnum.TopAbs_SOLID}).map((shape) => new Solid(shape));
}
/**
* Intersects the solid with one or more tools (other solids).
* @param {...Solid} tools - One or more `Solid` objects to intersect with.
* @returns {Solid[]} An array of `Solid` objects representing the result.
*/
intersect(...tools) {
const compound = intersect({
shape: this.#wrapped,
tools: tools.map((tool) => tool.wrapped),
});
return explore({shape: compound, find: oc.TopAbs_ShapeEnum.TopAbs_SOLID}).map((shape) => new Solid(shape));
}
/**
* Joins the solid with one or more tools (other solids).
* @param {...Solid} tools - One or more `Solid` objects to join with.
* @returns {Solid[]} An array of `Solid` objects representing the result.
*/
join(...tools) {
const compound = join({
shape: this.#wrapped,
tools: tools.map((tool) => tool.wrapped),
});
return explore({shape: compound, find: oc.TopAbs_ShapeEnum.TopAbs_SOLID}).map((shape) => new Solid(shape));
}
/**
* Applies a fillet (rounded edge) to selected edges of the solid.
* @param {Object} parameters - Fillet parameters.
* @param {function(Edge): boolean} parameters.selector - A function to select edges for the fillet.
* @param {number} parameters.radius - The radius of the fillet.
* @returns {Solid} A new `Solid` object with the fillet applied.
*/
fillet({selector, radius}) {
const fillet = new oc.BRepFilletAPI_MakeFillet(this.#wrapped, oc.ChFi3d_FilletShape.ChFi3d_Rational);
this.edges.filter(selector).forEach((edge) => fillet.Add_2(radius, edge.wrapped));
const compound = fillet.Shape();
const solids = explore({shape: compound, find: oc.TopAbs_ShapeEnum.TopAbs_SOLID});
return new Solid(solids[0]);
}
/**
* Applies a chamfer (beveled edge) to selected edges of the solid.
* @param {Object} parameters - Chamfer parameters.
* @param {function(Edge): boolean} parameters.selector - A function to select edges for the chamfer.
* @param {number} parameters.distance - The distance of the chamfer.
* @returns {Solid} A new `Solid` object with the chamfer applied.
*/
chamfer({selector, distance}) {
const chamfer = new oc.BRepFilletAPI_MakeChamfer(this.#wrapped);
this.edges.filter(selector).forEach((edge) => chamfer.Add_2(distance, edge.wrapped));
const compound = chamfer.Shape();
const solids = explore({shape: compound, find: oc.TopAbs_ShapeEnum.TopAbs_SOLID});
return new Solid(solids[0]);
}
/**
* Creates a hollow version of the solid by offsetting its faces by a specified thickness.
* @param {Object} parameters - Shell parameters.
* @param {function(Face): boolean} parameters.selector - A function to select faces for the shell.
* @param {number} parameters.thickness - The thickness of the shell. Positive value shells outwards, negative value shells inwards.
* @param {('arc'|'tangent'|'intersection')} [parameters.joinType="intersection"] - The join type for the shell.
* @returns {Solid} A new `Solid` object representing the shelled solid.
*/
shell({selector, thickness, joinType = "intersection"}) {
const wrappedFaces = new oc.TopTools_ListOfShape_1();
this.faces.filter(selector).forEach((face) => wrappedFaces.Append_1(face.wrapped));
const shell = new oc.BRepOffsetAPI_MakeThickSolid();
shell.MakeThickSolidByJoin(
this.#wrapped,
wrappedFaces,
thickness,
DEFAULT_PRECISION,
oc.BRepOffset_Mode.BRepOffset_Skin,
false,
false,
JOIN_TYPES[joinType],
true,
new oc.Message_ProgressRange_1()
);
return new Solid(shell.Shape());
}
/**
* Computes the hash code for the solid.
* @returns {number} The hash code.
* @private
*/
hashCode() {
return oc.OCJS.HashCode(this.#wrapped);
}
#surfaceProperties() {
if (!this.#properties) {
this.#properties = new oc.GProp_GProps_1();
oc.BRepGProp.VolumeProperties_1(this.#wrapped, this.#properties, true, false, false);
}
return this.#properties;
}
/**
* Creates a new solid from a set of faces.
* @param {...Face} faces - One or more `Face` objects to create the solid from.
* @returns {Solid} A new `Solid` object representing the created solid.
*/
static fromFaces(...faces) {
const sew = new oc.BRepBuilderAPI_Sewing(DEFAULT_PRECISION, true, true, true, false);
faces.forEach((face) => sew.Add(face.wrapped));
sew.Perform(new oc.Message_ProgressRange_1());
const builder = new oc.BRep_Builder();
const solid = new oc.TopoDS_Solid();
builder.MakeSolid(solid);
builder.Add(solid, sew.SewedShape());
return new Solid(solid);
}
}
cachedGetters({object: Solid, properties: ["faces", "edges", "vertices", "centerOfMass", "volume"]});