import oc from "../opencascade/initializer";
import explore from "../utils/explore";
import cachedGetters from "../utils/cache";
import {Solid, Face, Edge, Vertex} from "./index";
import {TRANSITION_MODES, JOIN_TYPES, DEFAULT_PRECISION} from "../constants";
import {sweep} from "../operations/features";
import {offset} from "../operations/modifications";
/**
* Represents a wire in 3D space.
* @memberof modeling
* @alias Wire
*/
export class Wire {
#wrapped;
/**
* @hideconstructor
*/
constructor(wrapped) {
this.#wrapped = oc.TopoDS.Wire_1(wrapped);
}
/**
* Returns the wrapped OpenCascade object.
* @private
*/
get wrapped() {
return this.#wrapped;
}
/**
* Retrieves all edges of the wire.
* @returns {Edge[]} An array of `Edge` objects representing the edges of the wire.
*/
get edges() {
return explore({shape: this.#wrapped, find: oc.TopAbs_ShapeEnum.TopAbs_EDGE}).map((shape) => new Edge(shape));
}
/**
* Retrieves all vertices of the wire.
* @returns {Vertex[]} An array of `Vertex` objects representing the vertices of the wire.
*/
get vertices() {
return explore({shape: this.#wrapped, find: oc.TopAbs_ShapeEnum.TopAbs_VERTEX}).map((shape) => new Vertex(shape));
}
/**
* Checks if the wire is closed.
* @returns {boolean} `true` if the wire is closed, `false` otherwise.
*/
get isClosed() {
return oc.BRep_Tool.IsClosed_1(this.wrapped);
}
/**
* Sweeps the specified profile along this wire to create a 3D solid.
* If the wire has sharp edges use transitionMode = "right".
* If the wire is C1 continuous use transitionMode = "transformed".
* @param {Object} parameters - Sweep parameters.
* @param {Face|Wire} parameters.profile - Profile to sweep along the wire.
* @param {Wire} [parameters.rail=null] - The guiding rail for the sweep.
* @param {('transformed'|'right'|'round')} [parameters.transitionMode="right"] - The transition mode for the sweep.
* @returns {Solid} A new `Solid` object representing the swept result.
*/
sweep({profile, rail = null, transitionMode = "right"}) {
if (profile instanceof Face) {
return this.#sweepFace({profile, rail, transitionMode});
} else if (profile instanceof Wire) {
return this.#sweepWires({profiles: [profile], rail, transitionMode});
} else {
return undefined;
}
}
/**
* Sweeps the specified profiles along this wire to create a 3D solid.
* If the wire has sharp edges use transitionMode = "right".
* If the wire is C1 continuous use transitionMode = "transformed".
* @param {Object} parameters - Sweep parameters.
* @param {Wire[]} parameters.profiles - Profiles to sweep along the wire.
* @param {Wire} [parameters.rail=null] - The guiding rail for the sweep.
* @param {('transformed'|'right'|'round')} [parameters.transitionMode="right"] - The transition mode for the sweep.
* @returns {Solid} A new `Solid` object representing the swept result.
*/
sweepMultiple({profiles, rail = null, transitionMode = "right"}) {
return this.#sweepWires({profiles, rail, transitionMode});
}
/**
* Offsets the wire by a specified distance.
* @param {Object} parameters - Offset parameters.
* @param {number} parameters.distance - The offset distance.
* @param {('arc'|'tangent'|'intersection')} [parameters.joinType="tangent"] - The join type for the offset.
* @returns {Face} A new `Wire` object representing the offset wire.
*/
offset({distance, joinType = "intersection"}) {
return new Wire(offset({wire: this.wrapped, distance, joinType: JOIN_TYPES[joinType], isOpen: !this.isClosed}));
}
#sweepFace({profile, rail, transitionMode = "right"}) {
const solids = [];
const wires = [profile.outerWire, ...profile.innerWires];
wires.forEach((wire) => {
solids.push(
new Solid(
sweep({
spine: this.wrapped,
profiles: [wire.wrapped],
auxiliarySpine: rail?.wrapped,
transitionMode: TRANSITION_MODES[transitionMode],
})
)
);
});
const [outerSolid, ...innerSolids] = solids;
return innerSolids.length == 0 ? outerSolid : outerSolid.cut(...innerSolids)[0];
}
#sweepWires({profiles, rail, transitionMode = "right"}) {
return new Solid(
sweep({
spine: this.wrapped,
profiles: profiles.map((profile) => profile.wrapped),
auxiliarySpine: rail?.wrapped,
transitionMode: TRANSITION_MODES[transitionMode],
})
);
}
/**
* Computes the hash code for the wire.
* @returns {number} The hash code.
* @private
*/
hashCode() {
return oc.OCJS.HashCode(this.#wrapped);
}
/**
* Creates a new `Wire` from the provided edges.
* @param {...Edge} edges - The edges to create the wire from.
* @returns {Wire} A new `Wire` object created from the provided edges.
*/
static fromEdges(...edges) {
const builder = new oc.BRepBuilderAPI_MakeWire_1();
edges.forEach((edge) => {
builder.Add_1(edge.wrapped);
});
return new Wire(builder.Wire());
}
/**
* Creates a loft between the wires or points to form a 3D solid.
* @param {Object} parameters - Loft parameters.
* @param {Vector} [parameters.start] - The starting point of the loft.
* @param {Face[]} parameters.wires - Wires to include in the loft.
* @param {Vector} [parameters.end] - The ending point of the loft.
* @returns {Solid} A new `Solid` object representing the lofted result.
*/
static loft({start, wires, end}) {
const loft = new oc.BRepOffsetAPI_ThruSections(true, true, DEFAULT_PRECISION);
if (start) {
const startPnt = new oc.gp_Pnt_3(start.x, start.y, start.z);
const startVertex = new oc.BRepBuilderAPI_MakeVertex(startPnt).Vertex();
loft.AddVertex(startVertex);
}
wires.forEach((wire) => loft.AddWire(wire.wrapped));
if (end) {
const endPnt = new oc.gp_Pnt_3(end.x, end.y, end.z);
const endVertex = new oc.BRepBuilderAPI_MakeVertex(endPnt).Vertex();
loft.AddVertex(endVertex);
}
return new Solid(loft.Shape());
}
}
cachedGetters({object: Wire, properties: ["edges", "vertices", "isClosed"]});