!2856 功能: 圆弧板的板件相切

pull/2876/MERGE
张子涵 3 months ago committed by ChenX
parent f4f3e3740c
commit f58b1d5b62

@ -0,0 +1,231 @@
import { Board } from "../../src/DatabaseServices/Entity/Board";
import { LoadEntityFromFileData } from "../Utils/LoadEntity.util";
// 测试命令(用于复制)npm run test -- ArcBoardSubtract.test.ts
test("刀:普通板 肉:圆弧板 结果:切断", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 100, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2844.615760812303, 714.0686896238476, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.615760812303, 714.0686896238476, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 2244.615760812303, "y": 714.0686896238476, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2376.0621081759596, -839.9555225862432, 600, 1] };
const d2 = { "file": [1, "Board", 10, 2, 970, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2844.615760812303, 1314.068689623848, -582, 1], 969, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.615760812303, 1314.068689623848, -582, 1], 0, 0, 1, 3, 1200, 731.1016555542986, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [731.1016555542986, 0], 0, [731.1016555542986, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 10, 2, 0, 0, 1, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 2230.124977120897, "y": 1314.0686896238476, "z": -582 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2376.0621081759596, -839.9555225862432, 600, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 普通切圆弧,在中点处切割
br2.Subtract([br1]);
expect(br2.Height).toBe(582);
expect(br2.Grooves.length).toBe(0);
});
test("刀:普通板 肉:圆弧板 结果:局部挖穿", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1010, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 2500.24935630908, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 2500.24935630908, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 2244.6157667616917, "y": 2500.24935630908, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1011, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2844.6157667616917, 2375.1560164326684, -582, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 2375.1560164326684, -582, 1], 0, 0, 1, 3, 1200, 731.1016555542986, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [731.1016555542986, 0], 0, [731.1016555542986, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 2230.1249830702855, "y": 2375.156016432668, "z": -582 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 普通切圆弧,在中点处切割
br2.Subtract([br1]);
expect(br2.Height).toBe(1200);
expect(br2.Grooves.length).toBe(1);
expect(br2.Grooves[0].Width).toBeCloseTo(376, 0);
expect(br2.Grooves[0].Height).toBe(18);
expect(br2.Grooves[0].Thickness).toBe(18);
});
test("刀:普通板 肉:圆弧板 切了又切 结果:不变", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1010, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 2500.24935630908, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 2500.24935630908, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 2244.6157667616917, "y": 2500.24935630908, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1011, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2844.6157667616917, 2375.1560164326684, -582, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 2375.1560164326684, -582, 1], 0, 0, 1, 3, 1200, 731.1016555542986, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [731.1016555542986, 0], 0, [731.1016555542986, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 2230.1249830702855, "y": 2375.156016432668, "z": -582 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 普通切圆弧,在中点处切割
br2.Subtract([br1]);
expect(br2.Height).toBe(1200);
expect(br2.Grooves.length).toBe(1);
expect(br2.Grooves[0].Width).toBeCloseTo(376, 0);
expect(br2.Grooves[0].Height).toBe(18);
expect(br2.Grooves[0].Thickness).toBe(18);
br2.Subtract([br1]);
expect(br2.Height).toBe(1200);
expect(br2.Grooves.length).toBe(1);
expect(br2.Grooves[0].Width).toBeCloseTo(376, 0);
expect(br2.Grooves[0].Height).toBe(18);
expect(br2.Grooves[0].Thickness).toBe(18);
});
test("刀:圆弧板 肉:普通板 结果:切断", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 100, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2844.615760812303, 714.0686896238476, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.615760812303, 714.0686896238476, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 2244.615760812303, "y": 714.0686896238476, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2376.0621081759596, -839.9555225862432, 600, 1] };
const d2 = { "file": [1, "Board", 10, 2, 970, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2844.615760812303, 1314.068689623848, -582, 1], 969, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.615760812303, 1314.068689623848, -582, 1], 0, 0, 1, 3, 1200, 731.1016555542986, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [731.1016555542986, 0], 0, [731.1016555542986, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 10, 2, 0, 0, 1, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 2230.124977120897, "y": 1314.0686896238476, "z": -582 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2376.0621081759596, -839.9555225862432, 600, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通,在中点处切割
br1.Subtract([br2]);
expect(br1.Width).toBeCloseTo(751.59, 0.01);
expect(br1.Grooves.length).toBe(0);
});
test("刀:圆弧板 肉:普通板 结果:挖槽", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1008, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 3918.3533328726858, 854.8219012550192, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3918.3533328726858, 854.8219012550192, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 1, 3, 600.0000000000002, 169.58831912099754, 9.680464072488007, false, "Polyline", 10, 2, 0, 0, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -600.0000000000002, 1.7053025658242404e-13, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -600.0000000000002, 1.7053025658242404e-13, 0, 1], 0, 0, 1, 2, 4, [600.0000000000005, 600], -0.5052943970699916, [600.0000000000005, 0], 0, [629.1535240575996, -5.684341886080802e-14], 0.4681159835446612, [629.1535240575993, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 3918.3533328726858, 1454.8219012550194, 8.319535927511993, 1], 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 3318.3533328726858, "y": 854.8219012550192, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2376.0621081759596, -839.9555225862432, 600, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1009, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 3918.3533328726858, 1454.8219012550196, 8.319535927511993, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3918.3533328726858, 1454.8219012550196, 8.319535927511993, 1], 0, 0, 1, 3, 1200, 731.1016555542986, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [731.1016555542986, 0], 0, [731.1016555542986, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 3303.8625491812795, "y": 1454.8219012550192, "z": 8.319535927511993 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2376.0621081759596, -839.9555225862432, 600, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通,在中点处切割
br1.Subtract([br2]);
expect(br1.Width).toBe(1200);
expect(br1.Grooves.length).toBe(1);
expect(br1.Grooves[0].Width).toBeCloseTo(169.58, 0.01);
expect(br1.Grooves[0].Height).toBeCloseTo(600, 0.01);
expect(br1.Grooves[0].Thickness).toBeCloseTo(9.68, 0.01);
});
test("刀:圆弧板 肉:普通板 结果:局部挖槽", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1012, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 3932.844116564092, -724.8340749354102, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3932.844116564092, -724.8340749354102, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 3332.844116564092, "y": -724.8340749354102, "z": 0 }, "ucs": [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1013, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 3932.844116564092, -124.83407493540972, 8.319535927511993, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3932.844116564092, -124.83407493540972, 8.319535927511993, 1], 0, 0, 1, 3, 1200.0000000000002, 731.1016555542988, 18, false, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 731.1016555542988, 1200.0000000000002, 0, 1], 0, 0, 1, [0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, -2416.101580598777, -1719.742665179213, 0, 1], 0, 0, 1, 2, 8, [-731.1016555542988, 0], 0, [-731.1016555542988, -1200.0000000000002], 0, [-554.9480555542982, -1200.0000000000002], 0, [-435.6008555542985, -958.6773333333331], 0, [-275.9336555542982, -958.6773333333331], 0, [-163.03765555429845, -1200.0000000000002], 0, [0, -1200.0000000000002], 0, [0, 0], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 8, 1, 1, 1, 1, 1, 1, 1, 1, "1", "1", "1", "1", "", "", "", 8, "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 8, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 3318.3533328726858, "y": -124.83409721823728, "z": 8.319535927512192 }, "ucs": [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧异形版(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通,在中点处切割
br1.Subtract([br2]);
expect(br1.Width).toBe(1200);
expect(br1.Grooves.length).toBe(2);
expect(br1.Grooves[0].Width).toBeCloseTo(120.48, 0.01);
expect(br1.Grooves[0].Height).toBeCloseTo(119.12, 0.01);
expect(br1.Grooves[0].Thickness).toBeCloseTo(9.68, 0.01);
expect(br1.Grooves[1].Width).toBeCloseTo(126.77, 0.01);
expect(br1.Grooves[1].Height).toBeCloseTo(130.37, 0.01);
expect(br1.Grooves[1].Thickness).toBeCloseTo(9.68, 0.01);
});
test("刀:圆弧板 肉:普通板 切了又切 结果:不变", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1012, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 3932.844116564092, -724.8340749354102, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3932.844116564092, -724.8340749354102, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 3332.844116564092, "y": -724.8340749354102, "z": 0 }, "ucs": [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1013, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 3932.844116564092, -124.83407493540972, 8.319535927511993, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3932.844116564092, -124.83407493540972, 8.319535927511993, 1], 0, 0, 1, 3, 1200.0000000000002, 731.1016555542988, 18, false, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 731.1016555542988, 1200.0000000000002, 0, 1], 0, 0, 1, [0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, -2416.101580598777, -1719.742665179213, 0, 1], 0, 0, 1, 2, 8, [-731.1016555542988, 0], 0, [-731.1016555542988, -1200.0000000000002], 0, [-554.9480555542982, -1200.0000000000002], 0, [-435.6008555542985, -958.6773333333331], 0, [-275.9336555542982, -958.6773333333331], 0, [-163.03765555429845, -1200.0000000000002], 0, [0, -1200.0000000000002], 0, [0, 0], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 8, 1, 1, 1, 1, 1, 1, 1, 1, "1", "1", "1", "1", "", "", "", 8, "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 8, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 3318.3533328726858, "y": -124.83409721823728, "z": 8.319535927512192 }, "ucs": [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧异形版(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通,在中点处切割
br1.Subtract([br2]);
expect(br1.Width).toBe(1200);
expect(br1.Grooves.length).toBe(2);
expect(br1.Grooves[0].Width).toBeCloseTo(120.48, 0.01);
expect(br1.Grooves[0].Height).toBeCloseTo(119.12, 0.01);
expect(br1.Grooves[0].Thickness).toBeCloseTo(9.68, 0.01);
expect(br1.Grooves[1].Width).toBeCloseTo(126.77, 0.01);
expect(br1.Grooves[1].Height).toBeCloseTo(130.37, 0.01);
expect(br1.Grooves[1].Thickness).toBeCloseTo(9.68, 0.01);
br1.Subtract([br2]);
expect(br1.Width).toBe(1200);
expect(br1.Grooves.length).toBe(2);
expect(br1.Grooves[0].Width).toBeCloseTo(120.48, 0.01);
expect(br1.Grooves[0].Height).toBeCloseTo(119.12, 0.01);
expect(br1.Grooves[0].Thickness).toBeCloseTo(9.68, 0.01);
expect(br1.Grooves[1].Width).toBeCloseTo(126.77, 0.01);
expect(br1.Grooves[1].Height).toBeCloseTo(130.37, 0.01);
expect(br1.Grooves[1].Thickness).toBeCloseTo(9.68, 0.01);
});
test("刀:圆弧板 肉:普通板 斜切 结果:切断", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1515, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 5079.387313671717, 782.5438387097381, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5079.387313671717, 782.5438387097381, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 4479.387313671717, "y": 782.5438387097381, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1516, 0, 1, 11, 71, [0, 0.9659258262890683, 0.25881904510252074, 0, 0, -0.25881904510252074, 0.9659258262890683, 0, 1, 0, 0, 0, 5079.387313671717, 1382.5438387097386, -582, 1], 0, 0, 1, [1, 0, 0, 0, 0, 0.9659258262890683, 0.25881904510252074, 0, 0, -0.25881904510252074, 0.9659258262890683, 0, 5079.387313671717, 1382.5438387097386, -582, 1], 0, 0, 1, 3, 1200, 731.1016555542986, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [731.1016555542986, 0], 0, [731.1016555542986, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 4464.89652998031, "y": 1071.9609845867133, "z": -582.0000000000001 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通二者比垂直多15°
br1.Subtract([br2]);
expect(br1.Width).toBeCloseTo(596, 0);
expect(br1.Grooves.length).toBe(0);
});
test("刀:圆弧板 肉:普通板 斜切 结果:切断", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 102, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1723.1818533625922, -1442.355647612756, 582, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1723.1818533625922, -1442.355647612756, 582, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 1123.1818533625922, "y": -1442.355647612756, "z": 582 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 10, 2, 103, 0, 1, 11, 71, [0, 0.788010753606722, 0.6156614753256583, 0, 0, -0.6156614753256583, 0.788010753606722, 0, 1, 0, 0, 0, 1723.1818533625922, -842.3556476127555, 5.684341886080802e-14, 1], 0, 0, 1, [1, 0, 0, 0, 0, 0.788010753606722, 0.6156614753256583, 0, 0, -0.6156614753256583, 0.788010753606722, 0, 1723.1818533625922, -842.3556476127555, 5.684341886080802e-14, 1], 0, 0, 1, 3, 1200, 731.1016555542986, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [731.1016555542986, 0], 0, [731.1016555542986, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 1108.691069671186, "y": -1581.1494180035459, "z": -1.7063255140878815e-13 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通二者比垂直多38°
br1.Subtract([br2]);
expect(br1.Width).toBeCloseTo(324, 0);
expect(br1.Grooves.length).toBe(0);
});
test("刀:圆弧板 肉:普通板 平切 结果:切断", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1519, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 3878.148959860686, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 3878.148959860686, 0, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 2244.6157667616917, "y": 3878.148959860686, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1520, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 4397.2640276069515, -5.684341886080802e-14, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2844.6157667616917, 4397.2640276069515, -5.684341886080802e-14, 1], 0, 0, 1, 3, 600, 715.3786607514678, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [715.3786607514678, 0], 0, [715.3786607514678, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 10, 2, 0, 0, 1, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -2244.6157667616917, -5232.533515779765, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.4674291876096676, [600, 0], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 715.37866, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 2244.6157667616917, "y": 4383.453943263995, "z": -4.263256414560601e-14 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:600, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通,二者平行
br1.Subtract([br2]);
expect(br1.Width).toBeCloseTo(505, 0);
expect(br1.Grooves.length).toBe(0);
});
test("刀:圆弧异形板 肉:普通板 结果:局部挖槽", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1529, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 5122.2705772876525, 3981.1323362110998, 18, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5122.2705772876525, 3981.1323362110998, 18, 1], 0, 0, 1, 3, 600, 1200, 18, true, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1200, 0], 0, [1200, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "", 0], "basePt": { "x": 4522.2705772876525, "y": 3981.1323362110998, "z": 18 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1530, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 5122.2705772876525, 4581.132336211101, 26.319535927511993, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5122.2705772876525, 4581.132336211101, 26.319535927511993, 1], 0, 0, 1, 3, 1200.4834200687515, 731.1016555542983, 18, false, "Polyline", 10, 2, 0, 0, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 731.1016555542983, 1200.4834200687515, 0, 1], 0, 0, 1, [0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, -5468.647583582021, -111.55040157304632, 0, 1], 0, 0, 1, 2, 8, [-731.1016555542983, -1009.5238095238094], 0.43521583508822825, [-526.5628800440936, -1200], 0, [-365.5508277771496, -1200], 0.41421356237309503, [-185.48542533917862, -1019.9345975620581], -1.000000010997757, [-79.2812473910326, -1019.9345975620581], 0.9999999999999999, [0, -1019.9345975620581], 0, [0, 0], 0, [-731.1016555542983, 0], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 3, 1, 1, 1, "1", "1", "1", "1", "", "", "", 8, "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 3, { "description": "" }, { "description": "" }, { "description": "" }, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 4507.779793596246, "y": 4581.132313928273, "z": 26.522560493685546 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200.48, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通,在中点处切割
br1.Subtract([br2]);
expect(br1.Width).toBe(1200);
expect(br1.Grooves.length).toBe(1);
expect(br1.Grooves[0].Width).toBeCloseTo(82.74, 0.01);
expect(br1.Grooves[0].Height).toBeCloseTo(275.55, 0.01);
expect(br1.Grooves[0].Thickness).toBeCloseTo(9.47, 0.01);
});
test("刀:圆弧异形板 肉:异形板 结果:局部挖槽", () =>
{
const d1 = { "file": [1, "Board", 10, 2, 1537, 0, 1, 2, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 6204.108299754569, 3981.1323362110998, 36, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 6204.108299754569, 3981.1323362110998, 36, 1], 0, 0, 1, 3, 600, 1200.0001828615464, 18, false, "Polyline", 10, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, -3985.213968864163, 7389.822585468852, -36, 1], 0, 0, 1, 2, 7, [0, 0], 0, [1200, 0], 0, [1200, 103.40136054421782], 0, [1063.9455782312925, 174.149659863946], 0, [1063.9455782312925, 300], 0.9055783921271827, [1064.3809523809532, 600], 0, [0, 600], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 0, "层板", "", "", "", "", "", 0, 0, "三合一", 2, 7, 1, 1, 1, 1, 1, 1, 1, "1", "1", "1", "1", "", "", "", 7, "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 7, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, { "description": "" }, false, 0, "", 0], "basePt": { "x": 5604.108299754569, "y": 3981.1323362110998, "z": 36 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 6204.108299754569, 3981.1323362110998, 36, 1] };
const d2 = { "file": [1, "Board", 10, 2, 1538, 0, 1, 11, 71, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 6204.108299754569, 4935.577903727245, 44.31953592751199, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 6204.108299754569, 4935.577903727245, 44.31953592751199, 1], 0, 0, 1, 3, 1200.4834200687515, 731.1016555542983, 18, false, "Polyline", 10, 2, 0, 0, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 731.1016555542983, 1200.4834200687515, 0, 1], 0, 0, 1, [0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, -5468.647583582021, -111.55040157304632, 0, 1], 0, 0, 1, 2, 8, [-731.1016555542983, -1009.5238095238094], 0.43521583508822825, [-526.5628800440936, -1200], 0, [-365.5508277771496, -1200], 0.41421356237309503, [-185.48542533917862, -1019.9345975620581], -1.000000010997757, [-79.2812473910326, -1019.9345975620581], 0.9999999999999999, [0, -1019.9345975620581], 0, [0, 0], 0, [-731.1016555542983, 0], 0, true, 0, 3, 0, 0, 0, 0, 0, 20, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 3, 1, 1, 1, "1", "1", "1", "1", "", "", "", 8, "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 3, { "description": "" }, { "description": "" }, { "description": "" }, false, 0, "Polyline", 10, 2, 0, 0, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0], "basePt": { "x": 5589.617516063163, "y": 4935.5778814444175, "z": 44.522560493685546 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 6204.108299754569, 3981.1323362110998, 36, 1] };
/** 普通板(高:600, 宽:1200, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 圆弧板(高:1200.48, 宽:731.1, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切普通,在中点处切割
br1.Subtract([br2]);
expect(br1.Width).toBeCloseTo(1200);
expect(br1.Grooves.length).toBe(2);
expect(br1.Grooves[0].Width).toBeCloseTo(55.62, 0.01);
expect(br1.Grooves[0].Height).toBeCloseTo(85.39, 0.01);
expect(br1.Grooves[0].Thickness).toBeCloseTo(8.46, 0.01);
expect(br1.Grooves[1].Width).toBeCloseTo(21.32, 0.01);
expect(br1.Grooves[1].Height).toBeCloseTo(44.80, 0.01);
expect(br1.Grooves[1].Thickness).toBeCloseTo(8.46, 0.01);
});
test("刀:圆弧板 肉:圆弧板 结果:切断", () =>
{
const d1 = { "file": [1, "Board", 11, 2, 1512, 0, 1, 11, 71, [0, 6.123233995736766e-17, 1, 0, 0, -1, 6.123233995736766e-17, 0, 1, 0, 0, 0, 6190.533374666166, 3776.6989303777405, 21.355782395544892, 1], 0, 0, 1, [1, 0, 0, 0, 0, 6.123233995736766e-17, 1, 0, 0, -1, 6.123233995736766e-17, 0, 6190.533374666166, 3776.6989303777405, 21.355782395544892, 1], 0, 0, 1, 0, 3, 1200, 731.1016555542986, 18, true, "Polyline", 11, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 0, 2, 4, [0, 0], 0, [731.1016555542986, 0], 0, [731.1016555542986, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 22, 1, "左侧板", "主卧", "下柜", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 11, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, -2108.7625671748674, -2844.615760812303, 0, 1], 0, 0, 1, 0, 2, 2, [0, 0], -0.5052943970699916, [0, -600], 0, false, 0, 0, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 731.10166, 6, 6, 2, 0, 0, 0, 3, 0, 0, 0], "basePt": { "x": 5576.04259097476, "y": 2576.6989303777405, "z": 21.355782395544523 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
const d2 = { "file": [1, "Board", 11, 2, 1513, 0, 1, 3, 71, [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 5562.505561677293, 3188.562685125938, -567.9663264066833, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5562.505561677293, 3188.562685125938, -567.9663264066833, 1], 0, 0, 1, 0, 3, 1200, 742.5233768947737, 18, true, "Polyline", 11, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 0, 2, 4, [0, 0], 0, [742.5233768947737, 0], 0, [742.5233768947737, 1200], 0, [0, 1200], 0, true, 0, 3, 0, 0, 0, 0, 0, 22, 2, "背板", "", "", "", "", "", 0, 0, "三合一", 2, 0, "1", "1", "1", "1", "", "", "", 4, "三合一", "三合一", "三合一", "三合一", true, true, 0, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, null, 0, 0, "", "", "", "", 0, false, 0, "Polyline", 11, 2, 0, 0, 0, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, -5562.505561677293, 3188.562685125938, 0, 1], 0, 0, 1, 0, 2, 2, [0, 0], 0.4532931428414304, [656.0556259777459, 4.547473508864641e-13], 0, false, 0, 1, true, 2, -1, 0, 6, 6, 2, 0, 0, 0, 3, 0, 0, 742.52338, 6, 6, 2, 0, 0, 0, 3, 0, 0, 0], "basePt": { "x": 5562.505561677293, "y": 3176.698930853477, "z": -567.9663264066833 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
/** 肉-圆弧板(高:1200.48, 宽:731.1, 厚:18 */
const br1 = LoadEntityFromFileData(d1)[0] as Board;
/** 刀-圆弧板(高:1200.48, 宽:742.52, 厚:18 */
const br2 = LoadEntityFromFileData(d2)[0] as Board;
// 圆弧切圆弧,在中点处切割
br1.Subtract([br2]);
expect(br1.Height).toBeCloseTo(559.01);
expect(br1.Grooves.length).toBe(0);
});

@ -54,7 +54,7 @@ function TestAll()
}, },
answer: { answer: {
count: 2, count: 2,
areas: [445744.72443803976, 394255.27556196053], areas: [440498.50473467575, 399501.49526532437],
lengths: [1343.4513322353837, 1343.4513322353837] lengths: [1343.4513322353837, 1343.4513322353837]
} }
}, },

@ -0,0 +1,444 @@
import geom3, { toPolygons } from "@jscad/modeling/src/geometries/geom3";
import { Geom3 } from "@jscad/modeling/src/geometries/types";
import { Mat4 } from "@jscad/modeling/src/maths/mat4";
import { measureAggregateVolume } from "@jscad/modeling/src/measurements";
import { intersect, scission } from "@jscad/modeling/src/operations/booleans";
import { Vector2, Vector3 } from "three";
import { CSGIntersect, Geom3Res } from "../../Common/CSGIntersect";
import { Board } from "../../DatabaseServices/Entity/Board";
import { ExtrudeSolid } from "../../DatabaseServices/Entity/Extrude";
import { Line } from "../../DatabaseServices/Entity/Line";
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { BSPGroupParse } from "../../Geometry/BSPGroupParse";
import { Box3Ext } from "../../Geometry/Box";
import { CreateContour2 } from "../../Geometry/CreateContour2";
import { MoveMatrix, equaln, equalv3 } from "../../Geometry/GeUtils";
/**
* @Finish
* 1.
* 2. 穿
* @TODO
* 1. (穿 )
* 2. , ()
* 3. ()
* 4. ()
* @Log 2024/06/20
* 1CSG
* @Log 2024/06/21
* :
* :
* @Log 2024/06/25
* : CSG
* : bug()
* @Log 2024/06/26
* 2CSG
* @Log 2024/06/27
* 2(1-2)project
* @Log 2024/07/02
* : project()
* @Log 2024/07/10
* :
* @Log 2024/07/11
* : C2R线project
* 3
*/
/** 针对圆弧板的板件切割*/
export class BoardCuttingForSweep
{
/** 生成圆弧板的切割器*/
constructor(private meat: Board, private knife: Board)
{
this.meat = meat;
this.knife = knife;
}
/** 生成凹槽 */
ConverToLocalGroove()
{
const meat = this.meat;
const knife = this.knife;
// 确保圆弧板构建成功
meat.MeshGeometry;
knife.MeshGeometry;
// 二者都是圆弧板
if (knife.IsArcBoard && meat.IsArcBoard)
return this.ArcCutArc();
// 刀是圆弧板
if (knife.IsArcBoard)
return this.ArcCutBoard();
// 肉是圆弧板
if (meat.IsArcBoard)
return this.BoardCutArc();
return [];
}
/** 圆弧板切普通板 */
private ArcCutBoard()
{
const meat = this.meat;
const knife = this.knife;
/** 生成轮廓-垂直切 */
const CreateContour = () =>
{
const contours: Polyline[] = [];
// 路径信息
const path1 = knife.GetSweepPath1InWCS().ApplyMatrix(meat.OCSInv);
const path2 = knife.GetSweepPath2InWCS().ApplyMatrix(meat.OCSInv);
path1.Z0();
path2.Z0();
// 不垂直
if (!equalv3(path1.Normal, new Vector3(0, 0, 1)) && !equalv3(path1.Normal, new Vector3(0, 0, -1)))
return CreateContourByOblique();
// CSG
const interCSG = CSGIntersect(meat.CSG, knife.CSG, meat.OCSInv.multiply(knife.OCSNoClone));
const interCSGs = scission(interCSG as unknown as Geom3) as unknown as Geom3[];
for (const interCSG of interCSGs)
{
// 过滤掉小于肉的1/1000体积的凹槽
const interVolume = measureAggregateVolume(interCSG);
const meatVolume = measureAggregateVolume(meat.CSG);
if (interVolume < meatVolume / 10000)
continue;
// 投影取点
const pts = this.ProjectCSGToPts(interCSG, "Z");
const range1 = this.GetParamRange(pts, path1);
const range2 = this.GetParamRange(pts, path2);
// 获取局部路径
const sp1 = path1.GetPointAtParam(range1.sParam);
const ep1 = path1.GetPointAtParam(range1.eParam);
const sp2 = path2.GetPointAtParam(range2.sParam);
const ep2 = path2.GetPointAtParam(range2.eParam);
const localPath1 = this.Get_Pl_InAtoB(path1, sp1, ep1);
const localPath2 = this.Get_Pl_InAtoB(path2, sp2, ep2);
// 围成轮廓
const line1 = new Line(localPath1.StartPoint, localPath2.StartPoint);
const line2 = new Line(localPath1.EndPoint, localPath2.EndPoint);
const contour = localPath1.Clone() as Polyline;
contour.Join(line1);
contour.Join(line2);
contour.Join(localPath2);
contours.push(contour);
}
return contours;
};
/** 生成轮廓-斜切(任意角度) */
const CreateContourByOblique = () =>
{
const contours: Polyline[] = [];
// CSG
const interCSG = CSGIntersect(meat.CSG, knife.CSG, meat.OCSInv.multiply(knife.OCSNoClone));
const interCSGs = scission(interCSG as unknown as Geom3) as unknown as Geom3[];
for (const interCSG of interCSGs)
{
// 过滤掉小于肉的1/1000体积的凹槽
const interVolume = measureAggregateVolume(interCSG);
const meatVolume = measureAggregateVolume(meat.CSG);
if (interVolume < meatVolume / 10000)
continue;
const contour = this.CSGToPolyline(interCSG);
contours.push(contour);
}
return contours;
};
/** 生成凹槽 */
const CreateGroove = (contourCurve: Polyline) =>
{
/** 凹槽 */
const groove = new ExtrudeSolid();
// @ts-ignore 直接修改即可(省略OCS转换)
groove.contourCurve = contourCurve;
// 获取凹槽的厚度
const interCSG = CSGIntersect(meat.CSG, knife.CSG, meat.OCSInv.multiply(knife.OCSNoClone));
const topology = new BSPGroupParse(interCSG);
const interBox = new Box3Ext();
for (let pts of topology.Parse())
interBox.setFromPoints(pts);
const interBoxSize = interBox.getSize(new Vector3());
groove.Thickness = interBoxSize.z;
// 修正位置
groove.OCS = meat.OCS;
if (interBox.min.z > 0)
groove.ApplyMatrix(MoveMatrix(meat.Normal.multiplyScalar(meat.Thickness - groove.Thickness)));
return groove;
};
const grooves: ExtrudeSolid[] = [];
// 1.生成轮廓
const grooveContours = CreateContour();
// 2.生成凹槽
for (const grooveContour of grooveContours)
{
const groove = CreateGroove(grooveContour);
grooves.push(groove);
}
return grooves;
}
/** 普通板切圆弧板 */
private BoardCutArc()
{
const meat = this.meat;
const knife = this.knife;
/** 生成轮廓-垂直切 */
const CreateContour = () =>
{
const contours: Polyline[] = [];
// 路径信息
const path = (meat as unknown as Board).GetSweepPathInWCS().ApplyMatrix(meat.OCSInv);
const tempPath = (meat as unknown as Board).GetSweepPathInWCS().ApplyMatrix(knife.OCSInv);
// 不垂直
if (!equalv3(tempPath.Normal, new Vector3(0, 0, 1)) && !equalv3(tempPath.Normal, new Vector3(0, 0, -1)))
return CreateContourByOblique();
// CSG
const csg1Clone = geom3.create(meat.CSG.polygons.concat());
const csg2Clone = geom3.create(knife.CSG.polygons.concat());
csg1Clone.transforms = meat.OCS.elements as Mat4;
csg2Clone.transforms = knife.OCS.elements as Mat4;
const interCSG = intersect(csg1Clone, csg2Clone);
interCSG.transforms = meat.OCSInv.elements as Mat4;
intersect(interCSG, interCSG);
const interCSGs = scission(interCSG as unknown as Geom3) as unknown as Geom3[];
for (const interCSG of interCSGs)
{
// 过滤掉小于肉的100体积的凹槽(这里甩固定值)
const interVolume = measureAggregateVolume(interCSG);
if (interVolume < 100)
continue;
const topology = new BSPGroupParse(interCSG as unknown as Geom3Res);
/** 交集部分的包围盒 */
const box = new Box3Ext(new Vector3(Infinity, Infinity, Infinity), new Vector3(-Infinity, -Infinity, -Infinity));
const ptsList = topology.Parse();
for (let pts of ptsList)
{
box.min.x = Math.min(box.min.x, ...pts.map(p => p.x));
box.min.y = Math.min(box.min.y, ...pts.map(p => p.y));
box.min.z = Math.min(box.min.z, ...pts.map(p => p.z));
box.max.x = Math.max(box.max.x, ...pts.map(p => p.x));
box.max.y = Math.max(box.max.y, ...pts.map(p => p.y));
box.max.z = Math.max(box.max.z, ...pts.map(p => p.z));
}
// 投影取点
const pts = this.ProjectCSGToPts(interCSG, "Y");
// 计算槽的长度
const range = this.GetParamRange(pts, path);
// 获取局部路径
const sDist1 = path.GetDistAtParam(range.sParam);
const eDist1 = path.GetDistAtParam(range.eParam);
/** 凹槽轮廓-多段线 */
const contour = new Polyline();
contour.LineData.push({ pt: new Vector2(sDist1, box.min.y), bul: 0 });
contour.LineData.push({ pt: new Vector2(eDist1, box.min.y), bul: 0 });
contour.LineData.push({ pt: new Vector2(eDist1, box.max.y), bul: 0 });
contour.LineData.push({ pt: new Vector2(sDist1, box.max.y), bul: 0 });
contour.LineData.push({ pt: new Vector2(sDist1, box.min.y), bul: 0 });
contours.push(contour);
}
return contours;
};
/** 生成轮廓-斜切(任意角度) */
const CreateContourByOblique = () =>
{
const contours: Polyline[] = [];
// CSG
const CSG = CSGIntersect(meat.CSG, knife.CSG, meat.OCSInv.multiply(knife.OCSNoClone)) as unknown as Geom3;
CSG.transforms = meat.OCS.elements as Mat4;
const interCSGs = scission(CSG as unknown as Geom3) as unknown as Geom3[];
for (const interCSG of interCSGs)
{
// 过滤掉小于肉的1/1000体积的凹槽
const interVolume = measureAggregateVolume(interCSG);
const meatVolume = measureAggregateVolume(meat.CSG);
if (interVolume < meatVolume / 10000)
continue;
// 对CSG进行逆映射
this.InverseCSG(interCSG);
const contour = this.CSGToPolyline(interCSG);
contours.push(contour);
}
return contours;
};
/** 生成凹槽 */
const CreateGroove = (contourCurve: Polyline) =>
{
/** 凹槽 */
const groove = new ExtrudeSolid();
// @ts-ignore 直接修改即可(省略OCS转换)
groove.contourCurve = contourCurve;
// TODO: 获取凹槽的厚度
groove.Thickness = meat.Thickness;
// 修正位置
groove.OCS = meat.OCS;
return groove;
};
const grooves: ExtrudeSolid[] = [];
const grooveContours = CreateContour();
for (const grooveContour of grooveContours)
{
/** 凹槽 */
const groove = CreateGroove(grooveContour);
grooves.push(groove);
}
return grooves;
}
/** 圆弧板切圆弧板 */
private ArcCutArc()
{
const meat = this.meat;
const knife = this.knife;
/** 生成轮廓 */
const CreateContour = () =>
{
const contours: Polyline[] = [];
// CSG
const CSG = CSGIntersect(meat.CSG, knife.CSG, meat.OCSInv.multiply(knife.OCSNoClone)) as unknown as Geom3;
CSG.transforms = meat.OCS.elements as Mat4;
const interCSGs = scission(CSG as unknown as Geom3) as unknown as Geom3[];
for (const interCSG of interCSGs)
{
// 过滤掉小于肉的1/1000体积的凹槽
const interVolume = measureAggregateVolume(interCSG);
const meatVolume = measureAggregateVolume(meat.CSG);
if (interVolume < meatVolume / 10000)
continue;
// 对CSG进行逆映射
this.InverseCSG(interCSG);
const contour = this.CSGToPolyline(interCSG);
contours.push(contour);
}
return contours;
};
/** 生成凹槽 */
const CreateGroove = (contourCurve: Polyline) =>
{
/** 凹槽 */
const groove = new ExtrudeSolid();
// @ts-ignore 直接修改即可(省略OCS转换)
groove.contourCurve = contourCurve;
// TODO: 获取凹槽的厚度
groove.Thickness = meat.Thickness;
// 修正位置
groove.OCS = meat.OCS;
return groove;
};
const grooves: ExtrudeSolid[] = [];
const grooveContours = CreateContour();
for (const grooveContour of grooveContours)
{
/** 凹槽 */
const groove = CreateGroove(grooveContour);
grooves.push(groove);
}
return grooves;
}
/** 对CSG进行逆映射(根据放样路径) */
private InverseCSG(interCSG: Geom3)
{
const meat = this.meat;
const pathWCS = meat.GetSweepPathInWCS();
const path = pathWCS.Clone().ApplyMatrix(pathWCS.OCSInv);
for (const polygon of interCSG.polygons)
{
for (const vertice of polygon.vertices)
{
const p = new Vector3(vertice[0], vertice[1], vertice[2]);
p.applyMatrix4(pathWCS.OCSInv);
const cp = path.GetClosestPointTo(p, false);
const param = Math.max(path.GetParamAtPoint(cp), 0);
const x = path.GetDistAtParam(param);
const y = -p.z;
p.y = y;
p.x = x;
vertice[0] = p.x;
vertice[1] = p.y;
}
}
}
/** 对CSG进行投影返回点集 */
private ProjectCSGToPts(CSG: Geom3, dir: "X" | "Y" | "Z" = "Z")
{
// 转为点集
const topology = new BSPGroupParse(CSG as unknown as Geom3Res);
const ps: Vector3[] = [];
for (let pts of topology.Parse())
{
for (const pt of pts)
{
const fuzz = 0.1;
// 对CSG进行投影
if (dir === "Z")
{
if (!ps.find(p => equaln(p.x, pt.x, fuzz) && equaln(p.y, pt.y, fuzz)))
{
pt.z = 0;
ps.push(pt);
}
}
else if (dir === "Y")
{
if (!ps.find(p => equaln(p.x, pt.x, fuzz) && equaln(p.z, pt.z, fuzz)))
{
pt.y = 0;
ps.push(pt);
}
}
}
}
return ps;
};
/** 对CSG进行投影返回多段线 */
private CSGToPolyline(CSG: Geom3)
{
const polygons = toPolygons(CSG);
const pls: Polyline[] = [];
for (const polygon of polygons)
{
const pl = new Polyline();
for (const v of polygon.vertices)
{
const pt = new Vector2(v[0], v[1]);
pl.LineData.push({ pt, bul: 0 });
pl.CloseMark = true; // 强制闭合
}
pls.push(pl);
}
return CreateContour2(pls).Curve as Polyline;
};
/** 获取CSG在路径上的param范围 */
private GetParamRange(pts: Vector3[], path: Polyline)
{
// 与路径匹配
const range = {
sParam: Infinity,
eParam: -Infinity
};
for (const pt of pts)
{
const cpt = path.GetClosestPointTo(pt, false);
const param = path.GetParamAtPoint(cpt);
if (param || param === 0)
{
range.sParam = Math.min(range.sParam, param);
range.eParam = Math.max(range.eParam, param);
}
}
// 避免应精度出现小于0的情况
range.sParam = Math.max(0, range.sParam);
return range;
}
/** 获取pA-pB之间的曲线 */
private Get_Pl_InAtoB(pl: Polyline, pA: Vector3, pB: Vector3)
{
const paramA = pl.GetParamAtPoint(pA);
const pls = pl.GetSplitCurves(paramA);
pl = pls[1] || pls[0];
const paramB = pl.GetParamAtPoint(pB);
const pls2 = pl.GetSplitCurves(paramB);
return pls2[0];
};
}

@ -201,7 +201,10 @@ export class LinearCutting implements Command
const startPoint = sweepPath.StartPoint; const startPoint = sweepPath.StartPoint;
sweepPath.Move(new Vector3(-startPoint.x, -startPoint.y, 0)); sweepPath.Move(new Vector3(-startPoint.x, -startPoint.y, 0));
const rotateMatrix = new Matrix4().extractRotation(br.OCS); const rotateMatrix = new Matrix4().extractRotation(br.OCS);
const biasV = new Vector3(startPoint.x, 0, startPoint.y).applyMatrix4(rotateMatrix); let biasV = new Vector3(startPoint.x, 0, startPoint.y).applyMatrix4(rotateMatrix);
// 针对放样角度的处理
if (equaln(sweepAngle, Math.PI / 2))
biasV = new Vector3(0, startPoint.x, startPoint.y).applyMatrix4(rotateMatrix);
const biasMatrix = new Matrix4().makeTranslation(biasV.x, biasV.y, biasV.z); const biasMatrix = new Matrix4().makeTranslation(biasV.x, biasV.y, biasV.z);
br.ApplyMatrix(biasMatrix); br.ApplyMatrix(biasMatrix);
// 旋转修正 // 旋转修正

@ -154,24 +154,29 @@ export class LinearCuttingForSweep
const basePt = splitData.pt; const basePt = splitData.pt;
const backPt = splitDatas[i - 1]?.pt; const backPt = splitDatas[i - 1]?.pt;
const nextPt = splitDatas[i + 1]?.pt; const nextPt = splitDatas[i + 1]?.pt;
// 目前先只针对首尾点
if (backPt && nextPt)
continue;
/** @todo 这里先按水平延伸来修正 */ /** @todo 这里先按水平延伸来修正 */
if (yLineMax.PtOnCurve(basePt)) if (yLineMax.PtOnCurve(basePt, 1e-3))
{ {
let sign = 1; let sign = 1;
if (backPt) sign = Math.sign(basePt.x - backPt.x); if (backPt) sign = Math.sign(basePt.x - backPt.x);
else if (nextPt) sign = Math.sign(basePt.x - nextPt.x); else if (nextPt) sign = Math.sign(basePt.x - nextPt.x);
const pt = basePt.clone().add(new Vector3(sign * 2000, 0, 0)); const pt = basePt.clone().add(new Vector3(sign * 2000, 0, 0));
const index = splitData.index + ((backPt && !yLineMax.PtOnCurve(backPt)) ? 0.01 : -0.01); const index = splitData.index + ((backPt && !yLineMax.PtOnCurve(backPt)) ? 0.01 : -0.01);
splitDatas.push({ pt, index }); splitDatas.unshift({ pt, index });
i++;
} }
else if (yLineMin.PtOnCurve(basePt)) else if (yLineMin.PtOnCurve(basePt, 1e-3))
{ {
let sign = 1; let sign = 1;
if (backPt) sign = Math.sign(basePt.x - backPt.x); if (backPt) sign = Math.sign(basePt.x - backPt.x);
else if (nextPt) sign = Math.sign(basePt.x - nextPt.x); else if (nextPt) sign = Math.sign(basePt.x - nextPt.x);
const pt = basePt.clone().add(new Vector3(sign * 2000, 0, 0)); const pt = basePt.clone().add(new Vector3(sign * 2000, 0, 0));
const index = splitData.index + ((backPt && !yLineMin.PtOnCurve(backPt)) ? 0.01 : -0.01); const index = splitData.index + ((backPt && !yLineMin.PtOnCurve(backPt)) ? 0.01 : -0.01);
splitDatas.push({ pt, index }); splitDatas.unshift({ pt, index });
i++;
} }
} }
// 排序 // 排序
@ -419,9 +424,7 @@ export class LinearCuttingForSweep
const inptMax = zLineMax.IntersectWith(sweepPathInWCS, IntersectOption.ExtendThis, 0.01)[0]; const inptMax = zLineMax.IntersectWith(sweepPathInWCS, IntersectOption.ExtendThis, 0.01)[0];
const inptMin = zLineMin.IntersectWith(sweepPathInWCS, IntersectOption.ExtendThis, 0.01)[0]; const inptMin = zLineMin.IntersectWith(sweepPathInWCS, IntersectOption.ExtendThis, 0.01)[0];
if (!inptMax || !inptMin) if (!inptMax || !inptMin)
{
return [zLineMax, zLineMin]; return [zLineMax, zLineMin];
}
const paramMax = sweepPathInWCS.GetParamAtPoint(inptMax); const paramMax = sweepPathInWCS.GetParamAtPoint(inptMax);
const paramMin = sweepPathInWCS.GetParamAtPoint(inptMin); const paramMin = sweepPathInWCS.GetParamAtPoint(inptMin);
/** 展开后的路径 */ /** 展开后的路径 */
@ -1036,7 +1039,7 @@ class EntityManager
/** 获取实体的包围盒线 */ /** 获取实体的包围盒线 */
static GetEnBoxPl(en: Entity) static GetEnBoxPl(en: Entity)
{ {
const box = en.Clone().ApplyMatrix(en.OCSInv).BoundingBox; const box = en.BoundingBox;
const boxPl = new Polyline([ const boxPl = new Polyline([
{ {
pt: new Vector2(box.min.x, box.min.y), pt: new Vector2(box.min.x, box.min.y),
@ -1059,7 +1062,7 @@ class EntityManager
bul: 0 bul: 0
}, },
]); ]);
boxPl.ApplyMatrix(en.OCS); boxPl.Z = box.min.z;
return boxPl; return boxPl;
}; };
} }

@ -53,6 +53,7 @@ import { Curve } from './Curve';
import { DragPointType } from './DragPointType'; import { DragPointType } from './DragPointType';
import { Entity } from './Entity'; import { Entity } from './Entity';
import { ExtrudeContourCurve, ExtrudeSolid } from './Extrude'; import { ExtrudeContourCurve, ExtrudeSolid } from './Extrude';
import { ExtrudeConfig } from './ExtrudeConfig';
import { GenLocalUv } from './GenLocalUv'; import { GenLocalUv } from './GenLocalUv';
import { Line } from './Line'; import { Line } from './Line';
import { Polyline } from './Polyline'; import { Polyline } from './Polyline';
@ -2262,6 +2263,14 @@ export class Board extends ExtrudeSolid
if (this._MeshGeometry) if (this._MeshGeometry)
return this._MeshGeometry; return this._MeshGeometry;
if (this.thickness <= 0)
return new BufferGeometry();
if (!ExtrudeConfig.DisableRefCut)
this.CalcRelevanceGroove();
if (this._MeshGeometry)
return this._MeshGeometry;
let build = new ArcBoardBuild(this); let build = new ArcBoardBuild(this);
[this._MeshGeometry, this._EdgeGeometry] = build.BuildMeshEdgeGeom(); [this._MeshGeometry, this._EdgeGeometry] = build.BuildMeshEdgeGeom();
this._SweepArcBoardBuild = build; this._SweepArcBoardBuild = build;

@ -3,6 +3,7 @@ import Flatbush from 'flatbush';
import { Box3, BoxGeometry, BufferGeometry, ExtrudeGeometry, ExtrudeGeometryOptions, Float32BufferAttribute, FrontSide, Frustum, Geometry, Group, InstancedInterleavedBuffer, InterleavedBufferAttribute, LineSegments, Material, Matrix3, Matrix4, Mesh, Object3D, Line as TLine, UVGenerator, Vector3 } from "three"; import { Box3, BoxGeometry, BufferGeometry, ExtrudeGeometry, ExtrudeGeometryOptions, Float32BufferAttribute, FrontSide, Frustum, Geometry, Group, InstancedInterleavedBuffer, InterleavedBufferAttribute, LineSegments, Material, Matrix3, Matrix4, Mesh, Object3D, Line as TLine, UVGenerator, Vector3 } from "three";
import { Line2 } from "three/examples/jsm/lines/Line2"; import { Line2 } from "three/examples/jsm/lines/Line2";
import { LineGeometry } from "three/examples/jsm/lines/LineGeometry"; import { LineGeometry } from "three/examples/jsm/lines/LineGeometry";
import { BoardCuttingForSweep } from '../../Add-on/BoardCutting/BoardCuttingForSweep';
import { arrayClone, arrayLast, arrayPushArray, arrayRemoveIf, arrayRemoveOnce, arraySortByNumber, arraySum } from "../../Common/ArrayExt"; import { arrayClone, arrayLast, arrayPushArray, arrayRemoveIf, arrayRemoveOnce, arraySortByNumber, arraySum } from "../../Common/ArrayExt";
import { CSGIntersect } from '../../Common/CSGIntersect'; import { CSGIntersect } from '../../Common/CSGIntersect';
import { ColorMaterial } from "../../Common/ColorPalette"; import { ColorMaterial } from "../../Common/ColorPalette";
@ -1299,6 +1300,13 @@ export class ExtrudeSolid extends Entity
*/ */
ConverToLocalGroove(target: ExtrudeSolid): ExtrudeSolid[] ConverToLocalGroove(target: ExtrudeSolid): ExtrudeSolid[]
{ {
// 针对圆弧板
if (target instanceof Board && this instanceof Board && (target.IsArcBoard || this.IsArcBoard))
{
const boardCuttingForSweep = new BoardCuttingForSweep(this, target);
return boardCuttingForSweep.ConverToLocalGroove();
}
if (!this.OBB.intersectsOBB(target.OBB)) return []; if (!this.OBB.intersectsOBB(target.OBB)) return [];
let n1 = this.Normal; let n1 = this.Normal;
@ -1964,7 +1972,7 @@ export class ExtrudeSolid extends Entity
/** /**
* ,(MeshGeometryEdgeGeometry) * ,(MeshGeometryEdgeGeometry)
*/ */
private CalcRelevanceGroove() protected CalcRelevanceGroove()
{ {
//避免Jig实体更新,导致性能暴跌. //避免Jig实体更新,导致性能暴跌.
if (!this.Id) return; if (!this.Id) return;
@ -1975,8 +1983,8 @@ export class ExtrudeSolid extends Entity
this.GetRelevanceKnifes(knifs); this.GetRelevanceKnifes(knifs);
//如果是切割圆弧板或刀是圆弧板,先不切割 //如果是切割圆弧板或刀是圆弧板,先不切割
if (this instanceof Board && this.IsArcBoard) knifs = []; // if (this instanceof Board && this.IsArcBoard) knifs = [];
knifs = knifs.filter(e => !(e instanceof Board && e.IsArcBoard)); // knifs = knifs.filter(e => !(e instanceof Board && e.IsArcBoard));
if (knifs.length > 0) if (knifs.length > 0)
{ {

Loading…
Cancel
Save