Skip to content

Commit a64eb0d

Browse files
dxf service unit tests
1 parent 62608b7 commit a64eb0d

File tree

1 file changed

+336
-0
lines changed

1 file changed

+336
-0
lines changed
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
import initOpenCascade, { OpenCascadeInstance, TopoDS_Edge, TopoDS_Shape } from "../../../bitbybit-dev-occt/bitbybit-dev-occt";
2+
import { OccHelper } from "../../occ-helper";
3+
import { VectorHelperService } from "../../api/vector-helper.service";
4+
import { ShapesHelperService } from "../../api/shapes-helper.service";
5+
import { OCCTWire, OCCTEdge } from "../shapes";
6+
import { DxfService } from "./dxf.service";
7+
import * as Inputs from "../../api/inputs/inputs";
8+
9+
describe("DxfService unit tests", () => {
10+
let occt: OpenCascadeInstance;
11+
let occHelper: OccHelper;
12+
let dxfService: DxfService;
13+
let wire: OCCTWire;
14+
let edge: OCCTEdge;
15+
16+
beforeAll(async () => {
17+
occt = await initOpenCascade();
18+
const vec = new VectorHelperService();
19+
const s = new ShapesHelperService();
20+
occHelper = new OccHelper(vec, s, occt);
21+
dxfService = occHelper.dxfService;
22+
wire = new OCCTWire(occt, occHelper);
23+
edge = new OCCTEdge(occt, occHelper);
24+
});
25+
26+
describe("tryCreatePolylineWithBulges", () => {
27+
// Helper to access private method
28+
const callTryCreatePolylineWithBulges = (
29+
service: DxfService,
30+
edges: TopoDS_Edge[],
31+
startIndex: number,
32+
shouldBeClosed: boolean
33+
) => {
34+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
35+
return (service as any).tryCreatePolylineWithBulges(edges, startIndex, shouldBeClosed);
36+
};
37+
38+
it("should return null for a single linear edge", () => {
39+
const lineEdge = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] });
40+
const edges = [lineEdge];
41+
42+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
43+
44+
// Returns null because we need at least 2 edges
45+
expect(result).toBeNull();
46+
47+
lineEdge.delete();
48+
});
49+
50+
it("should return null for a full circle edge", () => {
51+
const circleEdge = edge.createCircleEdge({
52+
radius: 5,
53+
center: [0, 0, 0],
54+
direction: [0, 1, 0]
55+
});
56+
const edges = [circleEdge];
57+
58+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
59+
60+
// Full circles are handled separately
61+
expect(result).toBeNull();
62+
63+
circleEdge.delete();
64+
});
65+
66+
it("should create a polyline from two consecutive linear edges", () => {
67+
const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] });
68+
const edge2 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [10, 0, 10] });
69+
const edges = [edge1, edge2];
70+
71+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
72+
73+
expect(result).not.toBeNull();
74+
expect(result.nextIndex).toBe(2);
75+
expect(result.polyline.points.length).toBe(3); // Start + mid + end
76+
expect(result.polyline.closed).toBe(false);
77+
78+
// All bulges should be 0 for linear edges
79+
result.polyline.bulges.forEach((bulge: number) => {
80+
expect(bulge).toBe(0);
81+
});
82+
83+
edge1.delete();
84+
edge2.delete();
85+
});
86+
87+
it("should create a polyline from two consecutive linear edges with closed flag", () => {
88+
const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] });
89+
const edge2 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [0, 0, 0] });
90+
const edges = [edge1, edge2];
91+
92+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, true);
93+
94+
expect(result).not.toBeNull();
95+
expect(result.polyline.closed).toBe(true);
96+
expect(result.polyline.points.length).toBe(3);
97+
98+
edge1.delete();
99+
edge2.delete();
100+
});
101+
102+
it("should create a polyline with non-zero bulges for arc edges", () => {
103+
const lineEdge = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] });
104+
const arcEdge = occHelper.edgesService.arcThroughThreePoints({
105+
start: [10, 0, 0],
106+
middle: [12, 0, 5],
107+
end: [10, 0, 10]
108+
});
109+
const edges = [lineEdge, arcEdge];
110+
111+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
112+
113+
expect(result).not.toBeNull();
114+
expect(result.nextIndex).toBe(2);
115+
expect(result.polyline.points.length).toBe(3);
116+
117+
// First bulge should be 0 (line), second should be non-zero (arc)
118+
expect(result.polyline.bulges[0]).toBe(0);
119+
expect(Math.abs(result.polyline.bulges[1])).toBeCloseTo(0.399999999);
120+
121+
lineEdge.delete();
122+
arcEdge.delete();
123+
});
124+
125+
it("should stop at complex edge", () => {
126+
const lineEdge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [5, 0, 0] });
127+
const lineEdge2 = occHelper.edgesService.lineEdge({ start: [5, 0, 0], end: [10, 0, 0] });
128+
129+
// Create a bezier/spline edge (complex edge)
130+
const bezierWire = wire.interpolatePoints({
131+
points: [[10, 0, 0], [12, 0, 3], [15, 0, 5]] as Inputs.Base.Point3[],
132+
periodic: false,
133+
tolerance: 0.0001
134+
});
135+
const bezierEdges = occHelper.edgesService.getEdgesAlongWire({ shape: bezierWire });
136+
137+
const lineEdge3 = occHelper.edgesService.lineEdge({ start: [15, 0, 5], end: [20, 0, 5] });
138+
139+
const edges = [lineEdge1, lineEdge2, ...bezierEdges, lineEdge3];
140+
141+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
142+
143+
expect(result).not.toBeNull();
144+
// Should stop at the complex bezier edge
145+
expect(result.nextIndex).toBe(2);
146+
expect(result.polyline.points.length).toBe(3);
147+
148+
lineEdge1.delete();
149+
lineEdge2.delete();
150+
bezierWire.delete();
151+
lineEdge3.delete();
152+
});
153+
154+
it("should return null when starting with a complex edge", () => {
155+
const bezierWire = wire.interpolatePoints({
156+
points: [[0, 0, 0], [5, 0, 3], [10, 0, 0]] as Inputs.Base.Point3[],
157+
periodic: false,
158+
tolerance: 0.0001
159+
});
160+
const bezierEdges = occHelper.edgesService.getEdgesAlongWire({ shape: bezierWire });
161+
162+
const result = callTryCreatePolylineWithBulges(dxfService, bezierEdges, 0, false);
163+
164+
// Complex edge at start should return null
165+
expect(result).toBeNull();
166+
167+
bezierWire.delete();
168+
});
169+
170+
it("should handle starting from a middle index", () => {
171+
const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [5, 0, 0] });
172+
const edge2 = occHelper.edgesService.lineEdge({ start: [5, 0, 0], end: [10, 0, 0] });
173+
const edge3 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [15, 0, 0] });
174+
const edge4 = occHelper.edgesService.lineEdge({ start: [15, 0, 0], end: [20, 0, 0] });
175+
const edges = [edge1, edge2, edge3, edge4];
176+
177+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 2, false);
178+
179+
expect(result).not.toBeNull();
180+
expect(result.nextIndex).toBe(4);
181+
expect(result.polyline.points.length).toBe(3); // edge3 start, edge4 start, edge4 end
182+
183+
// First point should be start of edge3
184+
expect(result.polyline.points[0][0]).toBeCloseTo(10, 2); // X
185+
expect(result.polyline.points[0][1]).toBeCloseTo(0, 2); // Z (mapped to Y in DXF)
186+
187+
edge1.delete();
188+
edge2.delete();
189+
edge3.delete();
190+
edge4.delete();
191+
});
192+
193+
it("should create polyline with correct point mapping from XZ to XY", () => {
194+
// Points at different Z values (which become Y in DXF)
195+
const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 5] });
196+
const edge2 = occHelper.edgesService.lineEdge({ start: [10, 0, 5], end: [20, 0, 10] });
197+
const edges = [edge1, edge2];
198+
199+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
200+
201+
expect(result).not.toBeNull();
202+
// Check X coordinates
203+
expect(result.polyline.points[0][0]).toBeCloseTo(0, 2);
204+
expect(result.polyline.points[1][0]).toBeCloseTo(10, 2);
205+
expect(result.polyline.points[2][0]).toBeCloseTo(20, 2);
206+
207+
// Check Y coordinates (originally Z in 3D)
208+
expect(result.polyline.points[0][1]).toBeCloseTo(0, 2);
209+
expect(result.polyline.points[1][1]).toBeCloseTo(5, 2);
210+
expect(result.polyline.points[2][1]).toBeCloseTo(10, 2);
211+
212+
edge1.delete();
213+
edge2.delete();
214+
});
215+
216+
it("should handle mixed linear and arc edges", () => {
217+
const line1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] });
218+
const arc = occHelper.edgesService.arcThroughThreePoints({
219+
start: [10, 0, 0],
220+
middle: [15, 0, 5],
221+
end: [10, 0, 10]
222+
});
223+
const line2 = occHelper.edgesService.lineEdge({ start: [10, 0, 10], end: [0, 0, 10] });
224+
const edges = [line1, arc, line2];
225+
226+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
227+
228+
expect(result).not.toBeNull();
229+
expect(result.nextIndex).toBe(3);
230+
expect(result.polyline.points.length).toBe(4);
231+
232+
// Check bulges: line=0, arc=non-zero, line=0, last=0
233+
expect(result.polyline.bulges[0]).toBe(0);
234+
expect(Math.abs(result.polyline.bulges[1])).toBeCloseTo(1);
235+
expect(result.polyline.bulges[2]).toBe(0);
236+
expect(result.polyline.bulges[3]).toBe(0);
237+
238+
line1.delete();
239+
arc.delete();
240+
line2.delete();
241+
});
242+
243+
it("should stop when encountering a full circle in the middle", () => {
244+
const line1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] });
245+
const line2 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [20, 0, 0] });
246+
const circleEdge = edge.createCircleEdge({
247+
radius: 3,
248+
center: [25, 0, 0],
249+
direction: [0, 1, 0]
250+
});
251+
const line3 = occHelper.edgesService.lineEdge({ start: [30, 0, 0], end: [40, 0, 0] });
252+
const edges = [line1, line2, circleEdge, line3];
253+
254+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
255+
256+
expect(result).not.toBeNull();
257+
// Should stop at the full circle
258+
expect(result.nextIndex).toBe(2);
259+
expect(result.polyline.points.length).toBe(3);
260+
261+
line1.delete();
262+
line2.delete();
263+
circleEdge.delete();
264+
line3.delete();
265+
});
266+
267+
it("should handle a closed rectangle with 4 edges", () => {
268+
const edge1 = occHelper.edgesService.lineEdge({ start: [0, 0, 0], end: [10, 0, 0] });
269+
const edge2 = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [10, 0, 5] });
270+
const edge3 = occHelper.edgesService.lineEdge({ start: [10, 0, 5], end: [0, 0, 5] });
271+
const edge4 = occHelper.edgesService.lineEdge({ start: [0, 0, 5], end: [0, 0, 0] });
272+
const edges = [edge1, edge2, edge3, edge4];
273+
274+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, true);
275+
276+
expect(result).not.toBeNull();
277+
expect(result.nextIndex).toBe(4);
278+
expect(result.polyline.closed).toBe(true);
279+
expect(result.polyline.points.length).toBe(5); // 4 corners + end point
280+
281+
// All bulges should be 0
282+
result.polyline.bulges.forEach((bulge: number) => {
283+
expect(bulge).toBe(0);
284+
});
285+
286+
edge1.delete();
287+
edge2.delete();
288+
edge3.delete();
289+
edge4.delete();
290+
});
291+
292+
it("should calculate correct bulge for a 90-degree arc", () => {
293+
// Create a 90-degree arc from (5, 0, 0) to (0, 0, 5) with center at origin, radius 5
294+
// This is a CCW 90-degree arc in the XZ plane
295+
const arcEdge = occHelper.edgesService.arcThroughThreePoints({
296+
start: [5, 0, 0],
297+
middle: [5 * Math.cos(Math.PI / 4), 0, 5 * Math.sin(Math.PI / 4)], // 45 degrees
298+
end: [0, 0, 5]
299+
});
300+
const lineEdge = occHelper.edgesService.lineEdge({ start: [0, 0, 5], end: [0, 0, 10] });
301+
const edges = [arcEdge, lineEdge];
302+
303+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
304+
305+
expect(result).not.toBeNull();
306+
307+
// For a 90-degree arc, bulge = tan(90/4) = tan(22.5°) ≈ 0.414
308+
// Sign depends on arc direction
309+
expect(Math.abs(result.polyline.bulges[0])).toBeCloseTo(0.414, 1);
310+
311+
arcEdge.delete();
312+
lineEdge.delete();
313+
});
314+
315+
it("should calculate correct bulge for a 180-degree arc (semicircle)", () => {
316+
// Create a semicircle arc
317+
const arcEdge = occHelper.edgesService.arcThroughThreePoints({
318+
start: [0, 0, 0],
319+
middle: [5, 0, 5],
320+
end: [10, 0, 0]
321+
});
322+
const lineEdge = occHelper.edgesService.lineEdge({ start: [10, 0, 0], end: [20, 0, 0] });
323+
const edges = [arcEdge, lineEdge];
324+
325+
const result = callTryCreatePolylineWithBulges(dxfService, edges, 0, false);
326+
327+
expect(result).not.toBeNull();
328+
329+
// For a 180-degree arc, bulge = tan(180/4) = tan(45°) = 1.0
330+
expect(Math.abs(result.polyline.bulges[0])).toBeCloseTo(1.0, 1);
331+
332+
arcEdge.delete();
333+
lineEdge.delete();
334+
});
335+
});
336+
});

0 commit comments

Comments
 (0)