Нумерация деталей
Перетащите сюда файл модели .b3d или нажмите, чтобы выбрать. Сайт пронумерует детали и покажет таблицу позиций. Файл с номерами можно скачать и открыть в Базисе - изменится только поле «Позиция» у деталей.
'); return h.join('\n'); } // ---------- Нумерация: группы одинаковых деталей (те же правила, что в проверке «Нумерация деталей») ---------- // Вставляется внутрь ядра MC (копия из «ПРОВЕРКИ ОБЩЕЙ»), пользуется его функциями (vec, V, dot, num, r1, words, hasToken, notFurniture). function partGroups(S, C, perModule) { var nskip = words(C.numSkip); var sheet = S.panels.filter(function (p) { return !p.hidden && p.ath >= 2 && p.ath <= 50 && !notFurniture(p) && !hasToken(p.mat + ' ' + p.name, nskip); }); var sigCache = new Map(), TOL = Math.max(0.0001, Number(C.numTol) || 0.01); var sigOf = function (p) { if (sigCache.has(p)) return sigCache.get(p); var W = p.w, H = p.h, feats = []; p.elems.forEach(function (e) { if (e.butt) feats.push(['e' + num(e.butt, 1), (e.p1[0] + e.p2[0]) / 2 - p.lmin[0], (e.p1[1] + e.p2[1]) / 2 - p.lmin[1]]); }); var zm = (p.z0 + p.z1) / 2; p.hi.forEach(function (i) { var h = S.holes[i], l = vec(p.ref.GlobalToObject(V(h.p[0], h.p[1], h.p[2]))); var face = Math.abs(dot(h.d, p.az)) > 0.8 ? (h.depth >= p.ath - 0.5 ? 'S' : l[2] < zm ? 'A' : 'B') : 'T'; feats.push(['h' + num(h.dia, 1) + face, l[0] - p.lmin[0], l[1] - p.lmin[1]]); }); if (H > W + 0.5) { feats = feats.map(function (f) { return [f[0], f[2], W - f[1]]; }); var t = W; W = H; H = t; } var s = { W: W, H: H, feats: feats }; sigCache.set(p, s); return s; }; var swapFace = function (c) { return c.replace(/A$/, '#').replace(/B$/, 'A').replace(/#$/, 'B'); }; var same = function (a, b) { var x = sigOf(a), y = sigOf(b); if (Math.abs(x.W - y.W) > TOL || Math.abs(x.H - y.H) > TOL || Math.abs(a.ath - b.ath) > TOL || a.mat.toLowerCase() !== b.mat.toLowerCase()) return false; if (x.feats.length !== y.feats.length) return false; var W = y.W, H = y.H, f = y.feats; var variants = [f, f.map(function (q) { return [q[0], W - q[1], H - q[2]]; }), f.map(function (q) { return [swapFace(q[0]), W - q[1], q[2]]; }), f.map(function (q) { return [swapFace(q[0]), q[1], H - q[2]]; })]; return variants.some(function (v) { var used = []; return x.feats.every(function (q) { for (var i = 0; i < v.length; i++) if (!used[i] && v[i][0] === q[0] && Math.abs(v[i][1] - q[1]) <= TOL && Math.abs(v[i][2] - q[2]) <= TOL) { used[i] = true; return true; } return false; }); }); }; // группы в порядке первой встречи (порядок деталей - как в структуре модели); одинаковые - ещё и с одинаковым названием var groups = []; sheet.forEach(function (p) { var nk = p.name.trim().toLowerCase().replace(/\s+/g, ' '); for (var i = 0; i < groups.length; i++) { var g = groups[i]; if (g.name === nk && (!perModule || g.mod === p.mod) && same(g.parts[0], p)) { g.parts.push(p); return; } } groups.push({ name: nk, mod: p.mod, parts: [p] }); }); return { groups: groups, skipped: S.panels.length - sheet.length }; } var partGroupsExport = partGroups; return { partGroups: partGroups, snapshot: snapshot, run: run, report: report, cncRenames: cncRenames, CHECKS: CHECKS, DEFAULTS: DEFAULTS, LOADS: LOADS, LEVELS: LEVELS, MACHINES: MACHINES }; })(); // ===================== Окно и нумерация ===================== (function () { var PROP_FILE = 'Нумерация.Werdan.prop'; var D = MC.DEFAULTS; var Prop = Action.Properties; var pOrder = Prop.NewCombo('Порядок', 'Как модули в структуре модели\nМодули слева направо'); var pFmt = Prop.NewCombo('Номера', 'Сквозные: 1, 2, 3 …\nС номером модуля: 1.1, 1.2, 2.1 …'); var pMode = Prop.NewCombo('Что нумеровать', 'Всё заново\nТолько детали без номера (остальные не трогать)'); var pStart = Prop.NewNumber('Начать с номера', 1); var pTol = Prop.NewNumber('Точность сравнения деталей, мм', D.numTol); var pSkip = Prop.NewString('Не нумеровать (материал или название содержит)'); pSkip.Value = D.numSkip; try { Prop.Load(PROP_FILE); } catch (e) { } function saveProps() { try { Prop.Save(PROP_FILE); } catch (e) { } } // выделение запоминается при запуске (как в остальных скриптах Werdan) var onlySel = false; try { Model.forEach(function (o) { if (o.Selected) onlySel = true; }); } catch (e) { } var undo = []; // [{ref, old}] - что было до последней нумерации function artOf(p) { try { return String(p.ref.ArtPos == null ? '' : p.ref.ArtPos).trim(); } catch (e) { return ''; } } function setArt(p, v) { var old = artOf(p); if (old === v) return false; try { StartEditing(p.ref); } catch (e) { } try { p.ref.ArtPos = v; undo.push({ ref: p.ref, old: old }); return true; } catch (e) { return false; } } function run() { saveProps(); var C = {}; for (var k in D) C[k] = D[k]; C.numTol = Number(pTol.Value) || D.numTol; C.numSkip = pSkip.Value; var S = MC.snapshot(Model, onlySel); var perModule = pFmt.ItemIndex == 1; var R = MC.partGroups(S, C, perModule), G = R.groups; if (!G.length) { alert('Деталей для нумерации не найдено' + (onlySel ? ' в выделенном' : '') + '.'); return; } // порядок модулей var rank = S.modules.map(function (m, i) { return i; }); if (pOrder.ItemIndex == 1) { var minX = S.modules.map(function () { return Infinity; }); S.panels.forEach(function (p) { if (!p.hidden) minX[p.mod] = Math.min(minX[p.mod], p.box.min[0]); }); var ord = S.modules.map(function (m, i) { return i; }).sort(function (a, b) { return minX[a] - minX[b] || a - b; }); ord.forEach(function (mi, r) { rank[mi] = r; }); } var firstIdx = function (g) { return S.panels.indexOf(g.parts[0]); }; G.sort(function (a, b) { return rank[a.mod] - rank[b.mod] || firstIdx(a) - firstIdx(b); }); undo = []; var start = Math.max(0, Math.round(Number(pStart.Value) || 1)), changed = 0, n = 0; if (pMode.ItemIndex == 1) { // только детали без номера: у группы уже есть номер - даём его остальным; новые группы - после самого большого номера var maxN = start - 1; S.panels.forEach(function (p) { var v = parseFloat(artOf(p)); if (isFinite(v) && v > maxN && String(artOf(p)).indexOf('.') < 0) maxN = Math.floor(v); }); var next = maxN + 1; G.forEach(function (g) { var have = ''; g.parts.forEach(function (p) { if (!have && artOf(p)) have = artOf(p); }); var v = have || String(next++); g.parts.forEach(function (p) { if (!artOf(p)) { if (setArt(p, v)) changed++; } }); }); n = next - maxN - 1; } else if (perModule) { var cnt = {}; G.forEach(function (g) { var m = rank[g.mod] + 1; cnt[m] = (cnt[m] || start - 1) + 1; var v = m + '.' + cnt[m]; g.parts.forEach(function (p) { if (setArt(p, v)) changed++; }); }); n = G.length; } else { G.forEach(function (g, i) { var v = String(start + i); g.parts.forEach(function (p) { if (setArt(p, v)) changed++; }); }); n = G.length; } try { Action.Commit(); } catch (e) { } var parts = 0; G.forEach(function (g) { parts += g.parts.length; }); try { Action.Hint = 'Позиций: ' + G.length + ', деталей: ' + parts + (pMode.ItemIndex == 1 ? ', новых номеров: ' + n : '') + ', изменено номеров: ' + changed + (R.skipped ? '; не нумеруются (стекло, окружение, не плита): ' + R.skipped : '') + '. Esc - закрыть'; } catch (e) { } } Prop.NewButton('Пронумеровать снова').OnClick = run; Prop.NewButton('Отменить').OnClick = function () { if (!undo.length) { try { Action.Hint = 'Отменять нечего'; } catch (e) { } return; } var k = 0; undo.forEach(function (u) { try { StartEditing(u.ref); u.ref.ArtPos = u.old; k++; } catch (e) { } }); undo = []; try { Action.Commit(); } catch (e) { } try { Action.Hint = 'Номера возвращены как были: ' + k; } catch (e) { } }; try { Action.OnFinish = function () { saveProps(); }; } catch (e) { } run(); Action.Continue(); })(); return { hint: hint, MC: MC }; }