diff --git a/hardware/bom/full_bom.csv b/hardware/bom/full_bom.csv index 44c5129..1e7225f 100755 --- a/hardware/bom/full_bom.csv +++ b/hardware/bom/full_bom.csv @@ -10,6 +10,6 @@ A-2200,4,R1 R2 R3 R4 ,1K,tayda A-2246,1,R5 ,220,tayda A-2248,1,R6 ,100K,tayda A-2247,1,R7 ,470,tayda -652-PTV09A-4025FB103,4,RV1 RV2 RV3 RV4 ,10k_pot,mouser +A-5531,4,RV1 RV2 RV3 RV4 ,10k_pot,tayda A-2470,1,U1 ,MCP3008,tayda 859-6N138,1,U2 ,6N138,mouser diff --git a/hardware/bom/ibom.html b/hardware/bom/ibom.html index 367ca4c..623d28e 100755 --- a/hardware/bom/ibom.html +++ b/hardware/bom/ibom.html @@ -9,9 +9,14 @@ :root { --pcb-edge-color: black; --pad-color: #878787; + --pad-hole-color: #CCCCCC; --pad-color-highlight: #D04040; + --pad-color-highlight-both: #D0D040; + --pad-color-highlight-marked: #44a344; --pin1-outline-color: #ffb629; - --pin1-outline-color-highlight: #b4ff03; + --pin1-outline-color-highlight: #ffb629; + --pin1-outline-color-highlight-both: #fcbb39; + --pin1-outline-color-highlight-marked: #fdbe41; --silkscreen-edge-color: #aa4; --silkscreen-polygon-color: #4aa; --silkscreen-text-color: #4aa; @@ -24,7 +29,8 @@ --zone-color-highlight: #d0404080; } -html, body { +html, +body { margin: 0px; height: 100%; font-family: Verdana, sans-serif; @@ -109,7 +115,7 @@ button#copy { } button#copy:active { - box-shadow: inset 0px 0px 5px #6c6c6c; + box-shadow: inset 0px 0px 5px #6c6c6c; } textarea.clipboard-temp { @@ -196,9 +202,11 @@ canvas:active { table-layout: fixed; width: 100%; margin-top: 1px; + position: relative; } -.bom th, .bom td { +.bom th, +.bom td { border: 1px solid black; padding: 5px; word-wrap: break-word; @@ -206,7 +214,8 @@ canvas:active { position: relative; } -.dark .bom th, .dark .bom td { +.dark .bom th, +.dark .bom td { border: 1px solid #777; } @@ -236,11 +245,11 @@ canvas:active { } .bom tr.checked { - color: #aaa; + color: #1cb53d; } .dark .bom tr.checked { - color: #666; + color: #2cce54; } .bom tr { @@ -248,22 +257,14 @@ canvas:active { } .bom .numCol { - width: 25px; + width: 30px; } -.bom .Description { - width: 10%; -} - -.bom .Part { - width: 10%; -} - -.bom .Value { +.bom .value { width: 15%; } -.bom .Quantity { +.bom .quantity { width: 65px; } @@ -325,7 +326,8 @@ canvas:active { visibility: hidden; } -.bom .bom-checkbox:hover:before, .bom .bom-checkbox:hover:after { +.bom .bom-checkbox:hover:before, +.bom .bom-checkbox:hover:after { visibility: visible; transition: visibility 0.2s linear 1s; } @@ -339,7 +341,8 @@ canvas:active { background-color: inherit; } -.split.split-horizontal, .gutter.gutter-horizontal { +.split.split-horizontal, +.gutter.gutter-horizontal { height: 100%; float: left; } @@ -452,7 +455,23 @@ mark.highlight { background-repeat: no-repeat; } -.dark .statsbtn, .dark .savebtn, .dark .menubtn, .dark .iobtn { +.visbtn { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath fill='none' stroke='%23333' d='M2.5 4.5h5v15h-5zM9.5 4.5h5v15h-5zM16.5 4.5h5v15h-5z'/%3E%3C/svg%3E"); + background-position: center; + background-repeat: no-repeat; + padding: 15px; +} + +#vismenu-content { + left: 0px; + font-family: Verdana, sans-serif; +} + +.dark .statsbtn, +.dark .savebtn, +.dark .menubtn, +.dark .iobtn, +.dark .visbtn { filter: invert(1); } @@ -524,6 +543,9 @@ mark.highlight { } .menu-content { + font-size: 12pt !important; + text-align: left !important; + font-weight: normal !important; display: none; position: absolute; background-color: white; @@ -542,7 +564,9 @@ mark.highlight { display: block; } -.menu:hover .menubtn, .menu:hover .iobtn, .menu:hover .statsbtn { +.menu:hover .menubtn, +.menu:hover .iobtn, +.menu:hover .statsbtn { background-color: #eee; } @@ -574,7 +598,8 @@ mark.highlight { width: calc(100% - 10px); } -.menu-textbox.invalid, .dark .menu-textbox.invalid { +.menu-textbox.invalid, +.dark .menu-textbox.invalid { color: red; } @@ -730,8 +755,50 @@ a { color: #00b9fd; } -#frontcanvas, #backcanvas { - touch-action: none; +#frontcanvas, +#backcanvas { + touch-action: none; +} + +.placeholder { + border: 1px dashed #9f9fda !important; + background-color: #edf2f7 !important; +} + +.dragging { + z-index: 999; +} + +.dark .dragging>table>tbody>tr { + background-color: #252c30; +} + +.dark .placeholder { + filter: invert(1); +} + +.column-spacer { + top: 0; + left: 0; + width: calc(100% - 4px); + position: absolute; + cursor: pointer; + user-select: none; + height: 100%; +} + +.column-width-handle { + top: 0; + right: 0; + width: 4px; + position: absolute; + cursor: col-resize; + user-select: none; + height: 100%; +} + +.column-width-handle:hover { + background-color: #4f99bd; } @@ -808,11 +875,11 @@ var d={x:c.clientX,y:c.clientY};b.push(d);var e=function(a,b){var c=a.indexOf(b) /////////////////////////////////////////////// /////////////////////////////////////////////// -var config = {"show_fabrication": false, "redraw_on_drag": true, "highlight_pin1": false, "extra_fields": [], "dark_mode": false, "bom_view": "left-right", "board_rotation": 0, "checkboxes": "Sourced,Placed", "show_silkscreen": true, "show_pads": true, "layer_view": "FB"} +var config = {"dark_mode": false, "show_pads": true, "show_fabrication": false, "show_silkscreen": true, "highlight_pin1": false, "redraw_on_drag": true, "board_rotation": 0, "checkboxes": "Sourced,Placed", "bom_view": "left-right", "layer_view": "FB", "fields": ["Value", "Footprint"]} /////////////////////////////////////////////// /////////////////////////////////////////////// -var pcbdata = JSON.parse(LZString.decompressFromBase64("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")) +var pcbdata = JSON.parse(LZString.decompressFromBase64("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")) /////////////////////////////////////////////// /////////////////////////////////////////////// @@ -979,25 +1046,27 @@ var units = { function initUtils() { var allPrefixes = units.prefixes.giga - .concat(units.prefixes.mega) - .concat(units.prefixes.kilo) - .concat(units.prefixes.milli) - .concat(units.prefixes.micro) - .concat(units.prefixes.nano) - .concat(units.prefixes.pico); + .concat(units.prefixes.mega) + .concat(units.prefixes.kilo) + .concat(units.prefixes.milli) + .concat(units.prefixes.micro) + .concat(units.prefixes.nano) + .concat(units.prefixes.pico); var allUnits = units.unitsShort.concat(units.unitsLong); units.valueRegex = new RegExp("^([0-9\.]+)" + - "\\s*(" + allPrefixes.join("|") + ")?" + - "(" + allUnits.join("|") + ")?" + - "(\\b.*)?$", ""); + "\\s*(" + allPrefixes.join("|") + ")?" + + "(" + allUnits.join("|") + ")?" + + "(\\b.*)?$", ""); units.valueAltRegex = new RegExp("^([0-9]*)" + - "(" + units.unitsShort.join("|") + ")?" + - "([GgMmKkUuNnPp])?" + - "([0-9]*)" + - "(\\b.*)?$", ""); - for (var bom_type of ["both", "F", "B"]) { - for (var row of pcbdata.bom[bom_type]) { - row.push(parseValue(row[1], row[3][0][0])); + "(" + units.unitsShort.join("|") + ")?" + + "([GgMmKkUuNnPp])?" + + "([0-9]*)" + + "(\\b.*)?$", ""); + if (config.fields.includes("Value")) { + var index = config.fields.indexOf("Value"); + pcbdata.bom["parsedValues"] = {}; + for (var id in pcbdata.bom.fields) { + pcbdata.bom.parsedValues[id] = parseValue(pcbdata.bom.fields[id][index]) } } } @@ -1141,7 +1210,9 @@ function saveSettings() { pcbmetadata: pcbdata.metadata, settings: settings, } - var blob = new Blob([JSON.stringify(data, null, 4)], {type: "application/json"}); + var blob = new Blob([JSON.stringify(data, null, 4)], { + type: "application/json" + }); saveFile(`${pcbdata.metadata.title}.settings.json`, blob); } @@ -1157,7 +1228,7 @@ function loadSettings() { var newSettings; try { newSettings = JSON.parse(content); - } catch(e) { + } catch (e) { alert("Selected file is not InteractiveHtmlBom settings file."); return; } @@ -1177,10 +1248,10 @@ function loadSettings() { var currentMetadata = JSON.stringify(pcbdata.metadata, null, 4); var fileMetadata = JSON.stringify(newSettings.pcbmetadata, null, 4); if (!confirm( - `Settins file metadata does not match current metadata.\n\n` + - `Page metadata:\n${currentMetadata}\n\n` + - `Settings file metadata:\n${fileMetadata}\n\n` + - `Press OK if you would like to import settings anyway.`)) { + `Settins file metadata does not match current metadata.\n\n` + + `Page metadata:\n${currentMetadata}\n\n` + + `Settings file metadata:\n${fileMetadata}\n\n` + + `Press OK if you would like to import settings anyway.`)) { return; } } @@ -1202,7 +1273,7 @@ function overwriteSettings(newSettings) { for (var checkbox of settings.checkboxes) { writeStorage("checkbox_" + checkbox, settings.checkboxStoredRefs[checkbox]); } - writeStorage("darkenWhenChecked", settings.darkenWhenChecked); + writeStorage("markWhenChecked", settings.markWhenChecked); padsVisible(settings.renderPads); document.getElementById("padsCheckbox").checked = settings.renderPads; fabricationVisible(settings.renderFabrication); @@ -1225,6 +1296,7 @@ function overwriteSettings(newSettings) { document.getElementById("darkmodeCheckbox").checked = settings.darkMode; setHighlightPin1(settings.highlightpin1); document.getElementById("highlightpin1Checkbox").checked = settings.highlightpin1; + showFootprints(settings.show_footprints); writeStorage("boardRotation", settings.boardRotation); document.getElementById("boardRotation").value = settings.boardRotation / 5; document.getElementById("rotationDegree").textContent = settings.boardRotation; @@ -1242,12 +1314,17 @@ function saveFile(filename, blob) { } function dataURLtoBlob(dataurl) { - var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], - bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); - while(n--){ - u8arr[n] = bstr.charCodeAt(n); + var arr = dataurl.split(','), + mime = arr[0].match(/:(.*?);/)[1], + bstr = atob(arr[1]), + n = bstr.length, + u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); } - return new Blob([u8arr], {type:mime}); + return new Blob([u8arr], { + type: mime + }); } var settings = { @@ -1268,6 +1345,8 @@ var settings = { renderDnpOutline: false, renderTracks: true, renderZones: true, + columnOrder: [], + hiddenColumns: [], } function initDefaults() { @@ -1296,8 +1375,8 @@ function initDefaults() { settings.checkboxes = bomCheckboxes.split(",").filter((e) => e); document.getElementById("bomCheckboxes").value = bomCheckboxes; - settings.darkenWhenChecked = readStorage("darkenWhenChecked") || ""; - populateDarkenWhenCheckedOptions(); + settings.markWhenChecked = readStorage("markWhenChecked") || ""; + populateMarkWhenCheckedOptions(); function initBooleanSetting(storageString, def, elementId, func) { var b = readStorage(storageString); @@ -1327,6 +1406,24 @@ function initDefaults() { initBooleanSetting("redrawOnDrag", config.redraw_on_drag, "dragCheckbox", setRedrawOnDrag); initBooleanSetting("darkmode", config.dark_mode, "darkmodeCheckbox", setDarkMode); initBooleanSetting("highlightpin1", config.highlight_pin1, "highlightpin1Checkbox", setHighlightPin1); + + var fields = ["checkboxes", "References"].concat(config.fields).concat(["Quantity"]); + var hcols = JSON.parse(readStorage("hiddenColumns")); + if (hcols === null) { + hcols = []; + } + settings.hiddenColumns = hcols.filter(e => fields.includes(e)); + + var cord = JSON.parse(readStorage("columnOrder")); + if (cord === null) { + cord = fields; + } else { + cord = cord.filter(e => fields.includes(e)); + if (cord.length != fields.length) + cord = fields; + } + settings.columnOrder = cord; + settings.boardRotation = readStorage("boardRotation"); if (settings.boardRotation === null) { settings.boardRotation = config.board_rotation * 5; @@ -1361,9 +1458,9 @@ const EventHandler = { args: eventArgs, } var callback; - for(callback of this.callbacks[eventType]) + for (callback of this.callbacks[eventType]) callback(event); - for(callback of this.callbacks[IBOM_EVENT_TYPES.ALL]) + for (callback of this.callbacks[IBOM_EVENT_TYPES.ALL]) callback(event); } } @@ -1390,7 +1487,7 @@ function calcFontPoint(linepoint, text, offsetx, offsety, tilt) { return point; } -function drawtext(ctx, text, color, flip) { +function drawText(ctx, text, color) { if ("ref" in text && !settings.renderReferences) return; if ("val" in text && !settings.renderValues) return; ctx.save(); @@ -1399,7 +1496,7 @@ function drawtext(ctx, text, color, flip) { ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.lineWidth = text.thickness; - if (text.svgpath) { + if ("svgpath" in text) { ctx.stroke(new Path2D(text.svgpath)); ctx.restore(); return; @@ -1485,9 +1582,11 @@ function drawtext(ctx, text, color, flip) { function drawedge(ctx, scalefactor, edge, color) { ctx.strokeStyle = color; + ctx.fillStyle = color; ctx.lineWidth = Math.max(1 / scalefactor, edge.width); ctx.lineCap = "round"; - if (edge.svgpath) { + ctx.lineJoin = "round"; + if ("svgpath" in edge) { ctx.stroke(new Path2D(edge.svgpath)); } else { ctx.beginPath(); @@ -1520,7 +1619,10 @@ function drawedge(ctx, scalefactor, edge, color) { ctx.moveTo(...edge.start); ctx.bezierCurveTo(...edge.cpa, ...edge.cpb, ...edge.end); } - ctx.stroke(); + if("filled" in edge && edge.filled) + ctx.fill(); + else + ctx.stroke(); } } @@ -1573,7 +1675,7 @@ function getPolygonsPath(shape) { if (shape.path2d) { return shape.path2d; } - if (shape.svgpath) { + if ("svgpath" in shape) { shape.path2d = new Path2D(shape.svgpath); } else { var path = new Path2D(); @@ -1589,24 +1691,32 @@ function getPolygonsPath(shape) { return shape.path2d; } -function drawPolygonShape(ctx, shape, color) { +function drawPolygonShape(ctx, scalefactor, shape, color) { ctx.save(); - ctx.fillStyle = color; - if (!shape.svgpath) { + if (!("svgpath" in shape)) { ctx.translate(...shape.pos); ctx.rotate(deg2rad(-shape.angle)); } - ctx.fill(getPolygonsPath(shape)); + if("filled" in shape && !shape.filled) { + ctx.strokeStyle = color; + ctx.lineWidth = Math.max(1 / scalefactor, shape.width); + ctx.lineCap = "round"; + ctx.lineJoin = "round"; + ctx.stroke(getPolygonsPath(shape)); + } else { + ctx.fillStyle = color; + ctx.fill(getPolygonsPath(shape)); + } ctx.restore(); } -function drawDrawing(ctx, layer, scalefactor, drawing, color) { - if (["segment", "arc", "circle", "curve"].includes(drawing.type)) { +function drawDrawing(ctx, scalefactor, drawing, color) { + if (["segment", "arc", "circle", "curve", "rect"].includes(drawing.type)) { drawedge(ctx, scalefactor, drawing, color); } else if (drawing.type == "polygon") { - drawPolygonShape(ctx, drawing, color); + drawPolygonShape(ctx, scalefactor, drawing, color); } else { - drawtext(ctx, drawing, color, layer == "B"); + drawText(ctx, drawing, color); } } @@ -1638,7 +1748,7 @@ function getCachedPadPath(pad) { return pad.path2d; } -function drawPad(ctx, pad, color, outline, hole) { +function drawPad(ctx, pad, color, outline) { ctx.save(); ctx.translate(...pad.pos); ctx.rotate(deg2rad(pad.angle)); @@ -1653,53 +1763,59 @@ function drawPad(ctx, pad, color, outline, hole) { } else { ctx.fill(path); } - if (pad.type == "th" && hole) { - if (pad.offset) { - ctx.translate(-pad.offset[0], -pad.offset[1]); - } - ctx.fillStyle = "#CCCCCC"; - if (pad.drillshape == "oblong") { - ctx.fill(getOblongPath(pad.drillsize)); - } else { - ctx.fill(getCirclePath(pad.drillsize[0] / 2)); - } + ctx.restore(); +} + +function drawPadHole(ctx, pad, padHoleColor) { + if (pad.type != "th") return; + ctx.save(); + ctx.translate(...pad.pos); + ctx.rotate(deg2rad(pad.angle)); + ctx.fillStyle = padHoleColor; + if (pad.drillshape == "oblong") { + ctx.fill(getOblongPath(pad.drillsize)); + } else { + ctx.fill(getCirclePath(pad.drillsize[0] / 2)); } ctx.restore(); } -function drawModule(ctx, layer, scalefactor, module, padcolor, outlinecolor, highlight, outline) { +function drawFootprint(ctx, layer, scalefactor, footprint, colors, highlight, outline) { if (highlight) { // draw bounding box - if (module.layer == layer) { + if (footprint.layer == layer) { ctx.save(); ctx.globalAlpha = 0.2; - ctx.translate(...module.bbox.pos); - ctx.rotate(deg2rad(-module.bbox.angle)); - ctx.translate(...module.bbox.relpos); - ctx.fillStyle = padcolor; - ctx.fillRect(0, 0, ...module.bbox.size); + ctx.translate(...footprint.bbox.pos); + ctx.rotate(deg2rad(-footprint.bbox.angle)); + ctx.translate(...footprint.bbox.relpos); + ctx.fillStyle = colors.pad; + ctx.fillRect(0, 0, ...footprint.bbox.size); ctx.globalAlpha = 1; - ctx.strokeStyle = padcolor; - ctx.strokeRect(0, 0, ...module.bbox.size); + ctx.strokeStyle = colors.pad; + ctx.strokeRect(0, 0, ...footprint.bbox.size); ctx.restore(); } } // draw drawings - for (var drawing of module.drawings) { + for (var drawing of footprint.drawings) { if (drawing.layer == layer) { - drawDrawing(ctx, layer, scalefactor, drawing.drawing, padcolor); + drawDrawing(ctx, scalefactor, drawing.drawing, colors.pad); } } // draw pads if (settings.renderPads) { - for (var pad of module.pads) { + for (var pad of footprint.pads) { if (pad.layers.includes(layer)) { - drawPad(ctx, pad, padcolor, outline, true); + drawPad(ctx, pad, colors.pad, outline); if (pad.pin1 && settings.highlightpin1) { - drawPad(ctx, pad, outlinecolor, true, false); + drawPad(ctx, pad, colors.outline, true); } } } + for (var pad of footprint.pads) { + drawPadHole(ctx, pad, colors.padHole); + } } } @@ -1707,38 +1823,53 @@ function drawEdgeCuts(canvas, scalefactor) { var ctx = canvas.getContext("2d"); var edgecolor = getComputedStyle(topmostdiv).getPropertyValue('--pcb-edge-color'); for (var edge of pcbdata.edges) { - drawedge(ctx, scalefactor, edge, edgecolor); + drawDrawing(ctx, scalefactor, edge, edgecolor); } } -function drawModules(canvas, layer, scalefactor, highlight) { +function drawFootprints(canvas, layer, scalefactor, highlight) { var ctx = canvas.getContext("2d"); ctx.lineWidth = 3 / scalefactor; var style = getComputedStyle(topmostdiv); - var padcolor = style.getPropertyValue('--pad-color'); - var outlinecolor = style.getPropertyValue('--pin1-outline-color'); - if (highlight) { - padcolor = style.getPropertyValue('--pad-color-highlight'); - outlinecolor = style.getPropertyValue('--pin1-outline-color-highlight'); + + var colors = { + pad: style.getPropertyValue('--pad-color'), + padHole: style.getPropertyValue('--pad-hole-color'), + outline: style.getPropertyValue('--pin1-outline-color'), } - for (var i = 0; i < pcbdata.modules.length; i++) { - var mod = pcbdata.modules[i]; + + for (var i = 0; i < pcbdata.footprints.length; i++) { + var mod = pcbdata.footprints[i]; var outline = settings.renderDnpOutline && pcbdata.bom.skipped.includes(i); - if (!highlight || highlightedModules.includes(i)) { - drawModule(ctx, layer, scalefactor, mod, padcolor, outlinecolor, highlight, outline); + var h = highlightedFootprints.includes(i); + var d = markedFootprints.has(i); + if (highlight) { + if(h && d) { + colors.pad = style.getPropertyValue('--pad-color-highlight-both'); + colors.outline = style.getPropertyValue('--pin1-outline-color-highlight-both'); + } else if (h) { + colors.pad = style.getPropertyValue('--pad-color-highlight'); + colors.outline = style.getPropertyValue('--pin1-outline-color-highlight'); + } else if (d) { + colors.pad = style.getPropertyValue('--pad-color-highlight-marked'); + colors.outline = style.getPropertyValue('--pin1-outline-color-highlight-marked'); + } + } + if( h || d || !highlight) { + drawFootprint(ctx, layer, scalefactor, mod, colors, highlight, outline); } } } function drawBgLayer(layername, canvas, layer, scalefactor, edgeColor, polygonColor, textColor) { var ctx = canvas.getContext("2d"); - for (var d of pcbdata[layername][layer]) { + for (var d of pcbdata.drawings[layername][layer]) { if (["segment", "arc", "circle", "curve", "rect"].includes(d.type)) { drawedge(ctx, scalefactor, d, edgeColor); } else if (d.type == "polygon") { - drawPolygonShape(ctx, d, polygonColor); + drawPolygonShape(ctx, scalefactor, d, polygonColor); } else { - drawtext(ctx, d, textColor, layer == "B"); + drawText(ctx, d, textColor); } } } @@ -1747,12 +1878,20 @@ function drawTracks(canvas, layer, color, highlight) { ctx = canvas.getContext("2d"); ctx.strokeStyle = color; ctx.lineCap = "round"; - for(var track of pcbdata.tracks[layer]) { + for (var track of pcbdata.tracks[layer]) { if (highlight && highlightedNet != track.net) continue; ctx.lineWidth = track.width; ctx.beginPath(); - ctx.moveTo(...track.start); - ctx.lineTo(...track.end); + if ('radius' in track) { + ctx.arc( + ...track.center, + track.radius, + deg2rad(track.startangle), + deg2rad(track.endangle)); + } else { + ctx.moveTo(...track.start); + ctx.lineTo(...track.end); + } ctx.stroke(); } } @@ -1762,7 +1901,7 @@ function drawZones(canvas, layer, color, highlight) { ctx.strokeStyle = color; ctx.fillStyle = color; ctx.lineJoin = "round"; - for(var zone of pcbdata.zones[layer]) { + for (var zone of pcbdata.zones[layer]) { if (!zone.path2d) { zone.path2d = getPolygonsPath(zone); } @@ -1783,7 +1922,8 @@ function clearCanvas(canvas, color = null) { ctx.fillStyle = color; ctx.fillRect(0, 0, canvas.width, canvas.height); } else { - ctx.clearRect(0, 0, canvas.width, canvas.height); + if (!window.matchMedia("print").matches) + ctx.clearRect(0, 0, canvas.width, canvas.height); } ctx.restore(); } @@ -1800,13 +1940,22 @@ function drawNets(canvas, layer, highlight) { } if (highlight && settings.renderPads) { var padColor = style.getPropertyValue('--pad-color-highlight'); + var padHoleColor = style.getPropertyValue('--pad-hole-color'); var ctx = canvas.getContext("2d"); - for (var mod of pcbdata.modules) { + for (var footprint of pcbdata.footprints) { // draw pads - for (var pad of mod.pads) { + var padDrawn = false; + for (var pad of footprint.pads) { if (highlightedNet != pad.net) continue; if (pad.layers.includes(layer)) { - drawPad(ctx, pad, padColor, false, true); + drawPad(ctx, pad, padColor, false); + padDrawn = true; + } + } + if (padDrawn) { + // redraw all pad holes because some pads may overlap + for (var pad of footprint.pads) { + drawPadHole(ctx, pad, padHoleColor); } } } @@ -1817,8 +1966,8 @@ function drawHighlightsOnLayer(canvasdict, clear = true) { if (clear) { clearCanvas(canvasdict.highlight); } - if (highlightedModules.length > 0) { - drawModules(canvasdict.highlight, canvasdict.layer, + if (markedFootprints.size > 0 || highlightedFootprints.length > 0) { + drawFootprints(canvasdict.highlight, canvasdict.layer, canvasdict.transform.s * canvasdict.transform.zoom, true); } if (highlightedNet !== null) { @@ -1839,10 +1988,10 @@ function drawBackground(canvasdict, clear = true) { } drawNets(canvasdict.bg, canvasdict.layer, false); - drawModules(canvasdict.bg, canvasdict.layer, + drawFootprints(canvasdict.bg, canvasdict.layer, canvasdict.transform.s * canvasdict.transform.zoom, false); - drawEdgeCuts(canvasdict.bg, canvasdict.transform.s); + drawEdgeCuts(canvasdict.bg, canvasdict.transform.s * canvasdict.transform.zoom); var style = getComputedStyle(topmostdiv); var edgeColor = style.getPropertyValue('--silkscreen-edge-color'); @@ -1990,6 +2139,39 @@ function pointWithinDistanceToSegment(x, y, x1, y1, x2, y2, d) { return dx * dx + dy * dy <= d * d; } +function modulo(n, mod) { + return ((n % mod) + mod) % mod; +} + +function pointWithinDistanceToArc(x, y, xc, yc, radius, startangle, endangle, d) { + var dx = x - xc; + var dy = y - yc; + var r_sq = dx * dx + dy * dy; + var rmin = Math.max(0, radius - d); + var rmax = radius + d; + + if (r_sq < rmin * rmin || r_sq > rmax * rmax) + return false; + + var angle1 = modulo(deg2rad(startangle), 2 * Math.PI); + var dx1 = xc + radius * Math.cos(angle1) - x; + var dy1 = yc + radius * Math.sin(angle1) - y; + if (dx1 * dx1 + dy1 * dy1 <= d * d) + return true; + + var angle2 = modulo(deg2rad(endangle), 2 * Math.PI); + var dx2 = xc + radius * Math.cos(angle2) - x; + var dy2 = yc + radius * Math.sin(angle2) - y; + if (dx2 * dx2 + dy2 * dy2 <= d * d) + return true; + + var angle = modulo(Math.atan2(dy, dx), 2 * Math.PI); + if (angle1 > angle2) + return (angle >= angle2 || angle <= angle1); + else + return (angle >= angle1 && angle <= angle2); +} + function pointWithinPad(x, y, pad) { var v = [x - pad.pos[0], y - pad.pos[1]]; v = rotateVector(v, -pad.angle); @@ -2003,16 +2185,22 @@ function pointWithinPad(x, y, pad) { function netHitScan(layer, x, y) { // Check track segments if (settings.renderTracks && pcbdata.tracks) { - for(var track of pcbdata.tracks[layer]) { - if (pointWithinDistanceToSegment(x, y, ...track.start, ...track.end, track.width / 2)) { - return track.net; + for (var track of pcbdata.tracks[layer]) { + if ('radius' in track) { + if (pointWithinDistanceToArc(x, y, ...track.center, track.radius, track.startangle, track.endangle, track.width / 2)) { + return track.net; + } + } else { + if (pointWithinDistanceToSegment(x, y, ...track.start, ...track.end, track.width / 2)) { + return track.net; + } } } } // Check pads if (settings.renderPads) { - for (var mod of pcbdata.modules) { - for(var pad of mod.pads) { + for (var footprint of pcbdata.footprints) { + for (var pad of footprint.pads) { if (pad.layers.includes(layer) && pointWithinPad(x, y, pad)) { return pad.net; } @@ -2022,19 +2210,19 @@ function netHitScan(layer, x, y) { return null; } -function pointWithinModuleBbox(x, y, bbox) { +function pointWithinFootprintBbox(x, y, bbox) { var v = [x - bbox.pos[0], y - bbox.pos[1]]; v = rotateVector(v, bbox.angle); return bbox.relpos[0] <= v[0] && v[0] <= bbox.relpos[0] + bbox.size[0] && - bbox.relpos[1] <= v[1] && v[1] <= bbox.relpos[1] + bbox.size[1]; + bbox.relpos[1] <= v[1] && v[1] <= bbox.relpos[1] + bbox.size[1]; } function bboxHitScan(layer, x, y) { var result = []; - for (var i = 0; i < pcbdata.modules.length; i++) { - var module = pcbdata.modules[i]; - if (module.layer == layer) { - if (pointWithinModuleBbox(x, y, module.bbox)) { + for (var i = 0; i < pcbdata.footprints.length; i++) { + var footprint = pcbdata.footprints[i]; + if (footprint.layer == layer) { + if (pointWithinFootprintBbox(x, y, footprint.bbox)) { result.push(i); } } @@ -2087,9 +2275,9 @@ function handleMouseClick(e, layerdict) { } } if (highlightedNet === null) { - var modules = bboxHitScan(layerdict.layer, ...v); - if (modules.length > 0) { - modulesClicked(modules); + var footprints = bboxHitScan(layerdict.layer, ...v); + if (footprints.length > 0) { + footprintsClicked(footprints); } } } @@ -2186,9 +2374,9 @@ function handlePointerMove(e, layerdict) { var otherPtr = Object.values(layerdict.pointerStates).filter((ptr) => ptr != thisPtr)[0]; var oldDist = Math.sqrt(Math.pow(thisPtr.lastX - otherPtr.lastX, 2) + Math.pow(thisPtr.lastY - otherPtr.lastY, 2)); - var newDist = Math.sqrt(Math.pow(e.offsetX - otherPtr.lastX, 2) + Math.pow(e.offsetY - otherPtr.lastY, 2)); + var newDist = Math.sqrt(Math.pow(e.offsetX - otherPtr.lastX, 2) + Math.pow(e.offsetY - otherPtr.lastY, 2)); - var scaleFactor = newDist/oldDist; + var scaleFactor = newDist / oldDist; if (scaleFactor != NaN) { layerdict.transform.zoom *= scaleFactor; @@ -2314,6 +2502,381 @@ function initRender() { /////////////////////////////////////////////// +/////////////////////////////////////////////// +/* + * Table reordering via Drag'n'Drop + * Inspired by: https://htmldom.dev/drag-and-drop-table-column + */ + +function setBomHandlers() { + + const bom = document.getElementById('bomtable'); + + let dragName; + let placeHolderElements; + let draggingElement; + let forcePopulation; + let xOffset; + let yOffset; + let wasDragged; + + const mouseUpHandler = function(e) { + // Delete dragging element + draggingElement.remove(); + + // Make BOM selectable again + bom.style.removeProperty("userSelect"); + + // Remove listeners + document.removeEventListener('mousemove', mouseMoveHandler); + document.removeEventListener('mouseup', mouseUpHandler); + + if (wasDragged) { + // Redraw whole BOM + populateBomTable(); + } + } + + const mouseMoveHandler = function(e) { + // Notice the dragging + wasDragged = true; + + // Make the dragged element visible + draggingElement.style.removeProperty("display"); + + // Set elements position to mouse position + draggingElement.style.left = `${e.screenX - xOffset}px`; + draggingElement.style.top = `${e.screenY - yOffset}px`; + + // Forced redrawing of BOM table + if (forcePopulation) { + forcePopulation = false; + // Copy array + phe = Array.from(placeHolderElements); + // populate BOM table again + populateBomHeader(dragName, phe); + populateBomBody(dragName, phe); + } + + // Set up array of hidden columns + var hiddenColumns = Array.from(settings.hiddenColumns); + // In the ungrouped mode, quantity don't exist + if (settings.bommode === "ungrouped") + hiddenColumns.push("Quantity"); + // If no checkbox fields can be found, we consider them hidden + if (settings.checkboxes.length == 0) + hiddenColumns.push("checkboxes"); + + // Get table headers and group them into checkboxes, extrafields and normal headers + const bh = document.getElementById("bomhead"); + headers = Array.from(bh.querySelectorAll("th")) + headers.shift() // numCol is not part of the columnOrder + headerGroups = [] + lastCompoundClass = null; + for (i = 0; i < settings.columnOrder.length; i++) { + cElem = settings.columnOrder[i]; + if (hiddenColumns.includes(cElem)) { + // Hidden columns appear as a dummy element + headerGroups.push([]); + continue; + } + elem = headers.filter(e => getColumnOrderName(e) === cElem)[0]; + if (elem.classList.contains("bom-checkbox")) { + if (lastCompoundClass === "bom-checkbox") { + cbGroup = headerGroups.pop(); + cbGroup.push(elem); + headerGroups.push(cbGroup); + } else { + lastCompoundClass = "bom-checkbox"; + headerGroups.push([elem]) + } + } else { + headerGroups.push([elem]) + } + } + + // Copy settings.columnOrder + var columns = Array.from(settings.columnOrder) + + // Set up array with indices of hidden columns + var hiddenIndices = hiddenColumns.map(e => settings.columnOrder.indexOf(e)); + var dragIndex = columns.indexOf(dragName); + var swapIndex = dragIndex; + var swapDone = false; + + // Check if the current dragged element is swapable with the left or right element + if (dragIndex > 0) { + // Get left headers boundingbox + swapIndex = dragIndex - 1; + while (hiddenIndices.includes(swapIndex) && swapIndex > 0) + swapIndex--; + if (!hiddenIndices.includes(swapIndex)) { + box = getBoundingClientRectFromMultiple(headerGroups[swapIndex]); + if (e.clientX < box.left + window.scrollX + (box.width / 2)) { + swapElement = columns[dragIndex]; + columns.splice(dragIndex, 1); + columns.splice(swapIndex, 0, swapElement); + forcePopulation = true; + swapDone = true; + } + } + } + if ((!swapDone) && dragIndex < headerGroups.length - 1) { + // Get right headers boundingbox + swapIndex = dragIndex + 1; + while (hiddenIndices.includes(swapIndex)) + swapIndex++; + if (swapIndex < headerGroups.length) { + box = getBoundingClientRectFromMultiple(headerGroups[swapIndex]); + if (e.clientX > box.left + window.scrollX + (box.width / 2)) { + swapElement = columns[dragIndex]; + columns.splice(dragIndex, 1); + columns.splice(swapIndex, 0, swapElement); + forcePopulation = true; + swapDone = true; + } + } + } + + // Write back change to storage + if (swapDone) { + settings.columnOrder = columns + writeStorage("columnOrder", JSON.stringify(columns)); + } + + } + + const mouseDownHandler = function(e) { + var target = e.target; + if (target.tagName.toLowerCase() != "td") + target = target.parentElement; + + // Used to check if a dragging has ever happened + wasDragged = false; + + // Create new element which will be displayed as the dragged column + draggingElement = document.createElement("div") + draggingElement.classList.add("dragging"); + draggingElement.style.display = "none"; + draggingElement.style.position = "absolute"; + draggingElement.style.overflow = "hidden"; + + // Get bomhead and bombody elements + const bh = document.getElementById("bomhead"); + const bb = document.getElementById("bombody"); + + // Get all compound headers for the current column + var compoundHeaders; + if (target.classList.contains("bom-checkbox")) { + compoundHeaders = Array.from(bh.querySelectorAll("th.bom-checkbox")); + } else { + compoundHeaders = [target]; + } + + // Create new table which will display the column + var newTable = document.createElement("table"); + newTable.classList.add("bom"); + newTable.style.background = "white"; + draggingElement.append(newTable); + + // Create new header element + var newHeader = document.createElement("thead"); + newTable.append(newHeader); + + // Set up array for storing all placeholder elements + placeHolderElements = []; + + // Add all compound headers to the new thead element and placeholders + compoundHeaders.forEach(function(h) { + clone = cloneElementWithDimensions(h); + newHeader.append(clone); + placeHolderElements.push(clone); + }); + + // Create new body element + var newBody = document.createElement("tbody"); + newTable.append(newBody); + + // Get indices for compound headers + var idxs = compoundHeaders.map(e => getBomTableHeaderIndex(e)); + + // For each row in the BOM body... + var rows = bb.querySelectorAll("tr"); + rows.forEach(function(row) { + // ..get the cells for the compound column + const tds = row.querySelectorAll("td"); + var copytds = idxs.map(i => tds[i]); + // Add them to the new element and the placeholders + var newRow = document.createElement("tr"); + copytds.forEach(function(td) { + clone = cloneElementWithDimensions(td); + newRow.append(clone); + placeHolderElements.push(clone); + }); + newBody.append(newRow); + }); + + // Compute width for compound header + var width = compoundHeaders.reduce((acc, x) => acc + x.clientWidth, 0); + draggingElement.style.width = `${width}px`; + + // Insert the new dragging element and disable selection on BOM + bom.insertBefore(draggingElement, null); + bom.style.userSelect = "none"; + + // Determine the mouse position offset + xOffset = e.screenX - compoundHeaders.reduce((acc, x) => Math.min(acc, x.offsetLeft), compoundHeaders[0].offsetLeft); + yOffset = e.screenY - compoundHeaders[0].offsetTop; + + // Get name for the column in settings.columnOrder + dragName = getColumnOrderName(target); + + // Change text and class for placeholder elements + placeHolderElements = placeHolderElements.map(function(e) { + newElem = cloneElementWithDimensions(e); + newElem.textContent = ""; + newElem.classList.add("placeholder"); + return newElem; + }); + + // On next mouse move, the whole BOM needs to be redrawn to show the placeholders + forcePopulation = true; + + // Add listeners for move and up on mouse + document.addEventListener('mousemove', mouseMoveHandler); + document.addEventListener('mouseup', mouseUpHandler); + } + + // In netlist mode, there is nothing to reorder + if (settings.bommode === "netlist") + return; + + // Add mouseDownHandler to every column except the numCol + bom.querySelectorAll("th") + .forEach(function(head) { + if (!head.classList.contains("numCol")) { + head.onmousedown = mouseDownHandler; + } + }); + +} + +function getBoundingClientRectFromMultiple(elements) { + var elems = Array.from(elements); + + if (elems.length == 0) + return null; + + var box = elems.shift() + .getBoundingClientRect(); + + elems.forEach(function(elem) { + var elembox = elem.getBoundingClientRect(); + box.left = Math.min(elembox.left, box.left); + box.top = Math.min(elembox.top, box.top); + box.width += elembox.width; + box.height = Math.max(elembox.height, box.height); + }); + + return box; +} + +function cloneElementWithDimensions(elem) { + var newElem = elem.cloneNode(true); + newElem.style.height = window.getComputedStyle(elem).height; + newElem.style.width = window.getComputedStyle(elem).width; + return newElem; +} + +function getBomTableHeaderIndex(elem) { + const bh = document.getElementById('bomhead'); + const ths = Array.from(bh.querySelectorAll("th")); + return ths.indexOf(elem); +} + +function getColumnOrderName(elem) { + var cname = elem.getAttribute("col_name"); + if (cname === "bom-checkbox") + return "checkboxes"; + else + return cname; +} + +function resizableGrid(tablehead) { + var cols = tablehead.firstElementChild.children; + var rowWidth = tablehead.offsetWidth; + + for (var i = 1; i < cols.length; i++) { + if (cols[i].classList.contains("bom-checkbox")) + continue; + cols[i].style.width = ((cols[i].clientWidth - paddingDiff(cols[i])) * 100 / rowWidth) + '%'; + } + + for (var i = 1; i < cols.length - 1; i++) { + var div = document.createElement('div'); + div.className = "column-width-handle"; + cols[i].appendChild(div); + setListeners(div); + } + + function setListeners(div) { + var startX, curCol, nxtCol, curColWidth, nxtColWidth, rowWidth; + + div.addEventListener('mousedown', function(e) { + e.preventDefault(); + e.stopPropagation(); + + curCol = e.target.parentElement; + nxtCol = curCol.nextElementSibling; + startX = e.pageX; + + var padding = paddingDiff(curCol); + + rowWidth = curCol.parentElement.offsetWidth; + curColWidth = curCol.clientWidth - padding; + nxtColWidth = nxtCol.clientWidth - padding; + }); + + document.addEventListener('mousemove', function(e) { + if (startX) { + var diffX = e.pageX - startX; + diffX = -Math.min(-diffX, curColWidth - 20); + diffX = Math.min(diffX, nxtColWidth - 20); + + curCol.style.width = ((curColWidth + diffX) * 100 / rowWidth) + '%'; + nxtCol.style.width = ((nxtColWidth - diffX) * 100 / rowWidth) + '%'; + console.log(`${curColWidth + nxtColWidth} ${(curColWidth + diffX) * 100 / rowWidth + (nxtColWidth - diffX) * 100 / rowWidth}`); + } + }); + + document.addEventListener('mouseup', function(e) { + curCol = undefined; + nxtCol = undefined; + startX = undefined; + nxtColWidth = undefined; + curColWidth = undefined + }); + } + + function paddingDiff(col) { + + if (getStyleVal(col, 'box-sizing') == 'border-box') { + return 0; + } + + var padLeft = getStyleVal(col, 'padding-left'); + var padRight = getStyleVal(col, 'padding-right'); + return (parseInt(padLeft) + parseInt(padRight)); + + } + + function getStyleVal(elm, css) { + return (window.getComputedStyle(elm, null).getPropertyValue(css)) + } +} + +/////////////////////////////////////////////// + /////////////////////////////////////////////// /* DOM manipulation and misc code */ @@ -2325,9 +2888,10 @@ var currentSortColumn = null; var currentSortOrder = null; var currentHighlightedRowId; var highlightHandlers = []; -var moduleIndexToHandler = {}; +var footprintIndexToHandler = {}; var netsToHandler = {}; -var highlightedModules = []; +var markedFootprints = new Set(); +var highlightedFootprints = []; var highlightedNet = null; var lastClicked; @@ -2389,6 +2953,37 @@ function setDarkMode(value) { redrawIfInitDone(); } +function setShowBOMColumn(field, value) { + if (field === "references") { + var rl = document.getElementById("reflookup"); + rl.disabled = !value; + if (!value) { + rl.value = ""; + updateRefLookup(""); + } + } + + var n = settings.hiddenColumns.indexOf(field); + if (value) { + if (n != -1) { + settings.hiddenColumns.splice(n, 1); + } + } else { + if (n == -1) { + settings.hiddenColumns.push(field); + } + } + + writeStorage("hiddenColumns", JSON.stringify(settings.hiddenColumns)); + + if (initDone) { + populateBomTable(); + } + + redrawIfInitDone(); +} + + function setFullscreen(value) { if (value) { document.documentElement.requestFullscreen(); @@ -2419,8 +3014,8 @@ function getStoredCheckboxRefs(checkbox) { function convert(ref) { var intref = parseInt(ref); if (isNaN(intref)) { - for (var i = 0; i < pcbdata.modules.length; i++) { - if (pcbdata.modules[i].ref == ref) { + for (var i = 0; i < pcbdata.footprints.length; i++) { + if (pcbdata.footprints[i].ref == ref) { return i; } } @@ -2467,9 +3062,9 @@ function setBomCheckboxState(checkbox, element, references) { } function createCheckboxChangeHandler(checkbox, references, row) { - return function() { + return function () { refsSet = getStoredCheckboxRefs(checkbox); - var darkenWhenChecked = settings.darkenWhenChecked == checkbox; + var markWhenChecked = settings.markWhenChecked == checkbox; eventArgs = { checkbox: checkbox, refs: references, @@ -2479,8 +3074,12 @@ function createCheckboxChangeHandler(checkbox, references, row) { for (var ref of references) { refsSet.add(ref[1]); } - if (darkenWhenChecked) { + if (markWhenChecked) { row.classList.add("checked"); + for (var ref of references) { + markedFootprints.add(ref[1]); + } + drawHighlights(); } eventArgs.state = 'checked'; } else { @@ -2488,8 +3087,12 @@ function createCheckboxChangeHandler(checkbox, references, row) { for (var ref of references) { refsSet.delete(ref[1]); } - if (darkenWhenChecked) { + if (markWhenChecked) { row.classList.remove("checked"); + for (var ref of references) { + markedFootprints.delete(ref[1]); + } + drawHighlights(); } eventArgs.state = 'unchecked'; } @@ -2500,17 +3103,17 @@ function createCheckboxChangeHandler(checkbox, references, row) { } } -function clearHighlightedModules() { +function clearHighlightedFootprints() { if (currentHighlightedRowId) { document.getElementById(currentHighlightedRowId).classList.remove("highlighted"); currentHighlightedRowId = null; - highlightedModules = []; + highlightedFootprints = []; highlightedNet = null; } } function createRowHighlightHandler(rowid, refs, net) { - return function() { + return function () { if (currentHighlightedRowId) { if (currentHighlightedRowId == rowid) { return; @@ -2519,16 +3122,15 @@ function createRowHighlightHandler(rowid, refs, net) { } document.getElementById(rowid).classList.add("highlighted"); currentHighlightedRowId = rowid; - highlightedModules = refs ? refs.map(r => r[1]) : []; + highlightedFootprints = refs ? refs.map(r => r[1]) : []; highlightedNet = net; drawHighlights(); EventHandler.emitEvent( - IBOM_EVENT_TYPES.HIGHLIGHT_EVENT, - { - rowid: rowid, - refs: refs, - net: net - }); + IBOM_EVENT_TYPES.HIGHLIGHT_EVENT, { + rowid: rowid, + refs: refs, + net: net + }); } } @@ -2538,30 +3140,29 @@ function entryMatches(entry) { return entry.toLowerCase().indexOf(filter) >= 0; } // check refs - for (var ref of entry[3]) { - if (ref[0].toLowerCase().indexOf(filter) >= 0) { - return true; + if (!settings.hiddenColumns.includes("references")) { + for (var ref of entry) { + if (ref[0].toLowerCase().indexOf(filter) >= 0) { + return true; + } } } - // check extra fields - for (var i in config.extra_fields) { - if (entry[4][i].toLowerCase().indexOf(filter) >= 0) { - return true; + // check fields + for (var i in config.fields) { + var f = config.fields[i]; + if (!settings.hiddenColumns.includes(f)) { + for (var ref of entry) { + if (pcbdata.bom.fields[ref[1]][i].toLowerCase().indexOf(filter) >= 0) { + return true; + } + } } } - // check value - if (entry[1].toLowerCase().indexOf(filter) >= 0) { - return true; - } - // check footprint - if (entry[2].toLowerCase().indexOf(filter) >= 0) { - return true; - } return false; } function findRefInEntry(entry) { - return entry[3].filter(r => r[0].toLowerCase() == reflookup); + return entry.filter(r => r[0].toLowerCase() == reflookup); } function highlightFilter(s) { @@ -2588,7 +3189,7 @@ function highlightFilter(s) { } function checkboxSetUnsetAllHandler(checkboxname) { - return function() { + return function () { var checkboxnum = 0; while (checkboxnum < settings.checkboxes.length && settings.checkboxes[checkboxnum].toLowerCase() != checkboxname.toLowerCase()) { @@ -2616,28 +3217,34 @@ function checkboxSetUnsetAllHandler(checkboxname) { } } -function createColumnHeader(name, cls, comparator) { +function createColumnHeader(name, cls, comparator, is_checkbox = false) { var th = document.createElement("TH"); th.innerHTML = name; th.classList.add(cls); - th.style.cursor = "pointer"; + if (is_checkbox) + th.setAttribute("col_name", "bom-checkbox"); + else + th.setAttribute("col_name", name); var span = document.createElement("SPAN"); span.classList.add("sortmark"); span.classList.add("none"); th.appendChild(span); - th.onclick = function() { - if (currentSortColumn && this !== currentSortColumn) { + var spacer = document.createElement("div"); + spacer.className = "column-spacer"; + th.appendChild(spacer); + spacer.onclick = function () { + if (currentSortColumn && th !== currentSortColumn) { // Currently sorted by another column currentSortColumn.childNodes[1].classList.remove(currentSortOrder); currentSortColumn.childNodes[1].classList.add("none"); currentSortColumn = null; currentSortOrder = null; } - if (currentSortColumn && this === currentSortColumn) { + if (currentSortColumn && th === currentSortColumn) { // Already sorted by this column if (currentSortOrder == "asc") { // Sort by this column, descending order - bomSortFunction = function(a, b) { + bomSortFunction = function (a, b) { return -comparator(a, b); } currentSortColumn.childNodes[1].classList.remove("asc"); @@ -2654,33 +3261,111 @@ function createColumnHeader(name, cls, comparator) { } else { // Sort by this column, ascending order bomSortFunction = comparator; - currentSortColumn = this; + currentSortColumn = th; currentSortColumn.childNodes[1].classList.remove("none"); currentSortColumn.childNodes[1].classList.add("asc"); currentSortOrder = "asc"; } populateBomBody(); } + if (is_checkbox) { + spacer.onclick = fancyDblClickHandler( + spacer, spacer.onclick, checkboxSetUnsetAllHandler(name)); + } return th; } -function populateBomHeader() { +function populateBomHeader(placeHolderColumn = null, placeHolderElements = null) { while (bomhead.firstChild) { bomhead.removeChild(bomhead.firstChild); } var tr = document.createElement("TR"); var th = document.createElement("TH"); th.classList.add("numCol"); + + var vismenu = document.createElement("div"); + vismenu.id = "vismenu"; + vismenu.classList.add("menu"); + + var visbutton = document.createElement("div"); + visbutton.classList.add("visbtn"); + visbutton.classList.add("hideonprint"); + + var viscontent = document.createElement("div"); + viscontent.classList.add("menu-content"); + viscontent.id = "vismenu-content"; + + settings.columnOrder.forEach(column => { + if (typeof column !== "string") + return; + + // Skip empty columns + if (column === "checkboxes" && settings.checkboxes.length == 0) + return; + else if (column === "Quantity" && settings.bommode == "ungrouped") + return; + + var label = document.createElement("label"); + label.classList.add("menu-label"); + + var input = document.createElement("input"); + input.classList.add("visibility_checkbox"); + input.type = "checkbox"; + input.onchange = function (e) { + setShowBOMColumn(column, e.target.checked) + }; + input.checked = !(settings.hiddenColumns.includes(column)); + + label.appendChild(input); + if (column.length > 0) + label.append(column[0].toUpperCase() + column.slice(1)); + + viscontent.appendChild(label); + }); + + viscontent.childNodes[0].classList.add("menu-label-top"); + + vismenu.appendChild(visbutton); + if (settings.bommode != "netlist") { + vismenu.appendChild(viscontent); + th.appendChild(vismenu); + } tr.appendChild(th); - var checkboxCompareClosure = function(checkbox) { + + var checkboxCompareClosure = function (checkbox) { return (a, b) => { - var stateA = getCheckboxState(checkbox, a[3]); - var stateB = getCheckboxState(checkbox, b[3]); + var stateA = getCheckboxState(checkbox, a); + var stateB = getCheckboxState(checkbox, b); if (stateA > stateB) return -1; if (stateA < stateB) return 1; return 0; } } + var stringFieldCompareClosure = function (fieldIndex) { + return (a, b) => { + var fa = pcbdata.bom.fields[a[0][1]][fieldIndex]; + var fb = pcbdata.bom.fields[b[0][1]][fieldIndex]; + if (fa != fb) return fa > fb ? 1 : -1; + else return 0; + } + } + var referenceRegex = /(?[^0-9]+)(?[0-9]+)/; + var compareRefs = (a, b) => { + var ra = referenceRegex.exec(a); + var rb = referenceRegex.exec(b); + if (ra === null || rb === null) { + if (a != b) return a > b ? 1 : -1; + return 0; + } else { + if (ra.groups.prefix != rb.groups.prefix) { + return ra.groups.prefix > rb.groups.prefix ? 1 : -1; + } + if (ra.groups.number != rb.groups.number) { + return parseInt(ra.groups.number) > parseInt(rb.groups.number) ? 1 : -1; + } + return 0; + } + } if (settings.bommode == "netlist") { th = createColumnHeader("Net name", "bom-netname", (a, b) => { if (a > b) return -1; @@ -2689,58 +3374,69 @@ function populateBomHeader() { }); tr.appendChild(th); } else { - for (var checkbox of settings.checkboxes) { - th = createColumnHeader( - checkbox, "bom-checkbox", checkboxCompareClosure(checkbox)); - th.onclick = fancyDblClickHandler( - th, th.onclick.bind(th), checkboxSetUnsetAllHandler(checkbox)); - tr.appendChild(th); - } - tr.appendChild(createColumnHeader("References", "References", (a, b) => { - var i = 0; - while (i < a[3].length && i < b[3].length) { - if (a[3][i] != b[3][i]) return a[3][i] > b[3][i] ? 1 : -1; - i++; - } - return a[3].length - b[3].length; - })); - // Extra fields - if (config.extra_fields.length > 0) { - var extraFieldCompareClosure = function(fieldIndex) { - return (a, b) => { - var fa = a[4][fieldIndex]; - var fb = b[4][fieldIndex]; - if (fa != fb) return fa > fb ? 1 : -1; - else return 0; + // Filter hidden columns + var columns = settings.columnOrder.filter(e => !settings.hiddenColumns.includes(e)); + var valueIndex = config.fields.indexOf("Value"); + var footprintIndex = config.fields.indexOf("Footprint"); + columns.forEach((column) => { + if (column === placeHolderColumn) { + var n = 1; + if (column === "checkboxes") + n = settings.checkboxes.length; + for (i = 0; i < n; i++) { + td = placeHolderElements.shift(); + tr.appendChild(td); } - } - for (var i in config.extra_fields) { + return; + } else if (column === "checkboxes") { + for (var checkbox of settings.checkboxes) { + th = createColumnHeader( + checkbox, "bom-checkbox", checkboxCompareClosure(checkbox), true); + tr.appendChild(th); + } + } else if (column === "References") { + tr.appendChild(createColumnHeader("References", "references", (a, b) => { + var i = 0; + while (i < a.length && i < b.length) { + if (a[i] != b[i]) return compareRefs(a[i][0], b[i][0]); + i++; + } + return a.length - b.length; + })); + } else if (column === "Value") { + tr.appendChild(createColumnHeader("Value", "value", (a, b) => { + var ra = a[0][1], rb = b[0][1]; + return valueCompare( + pcbdata.bom.parsedValues[ra], pcbdata.bom.parsedValues[rb], + pcbdata.bom.fields[ra][valueIndex], pcbdata.bom.fields[rb][valueIndex]); + })); + return; + } else if (column === "Footprint") { tr.appendChild(createColumnHeader( - config.extra_fields[i], "extra", extraFieldCompareClosure(i))); + "Footprint", "footprint", stringFieldCompareClosure(footprintIndex))); + } else if (column === "Quantity" && settings.bommode == "grouped") { + tr.appendChild(createColumnHeader("Quantity", "quantity", (a, b) => { + return a.length - b.length; + })); + } else { + // Other fields + var i = config.fields.indexOf(column); + if (i < 0) + return; + tr.appendChild(createColumnHeader( + column, `field${i + 1}`, stringFieldCompareClosure(i))); } - } - tr.appendChild(createColumnHeader("Value", "Value", (a, b) => { - return valueCompare(a[5], b[5], a[1], b[1]); - })); - tr.appendChild(createColumnHeader("Footprint", "Footprint", (a, b) => { - if (a[2] != b[2]) return a[2] > b[2] ? 1 : -1; - else return 0; - })); - if (settings.bommode == "grouped") { - tr.appendChild(createColumnHeader("Quantity", "Quantity", (a, b) => { - return a[3].length - b[3].length; - })); - } + }); } bomhead.appendChild(tr); } -function populateBomBody() { +function populateBomBody(placeholderColumn = null, placeHolderElements = null) { while (bom.firstChild) { bom.removeChild(bom.firstChild); } highlightHandlers = []; - moduleIndexToHandler = {}; + footprintIndexToHandler = {}; netsToHandler = {}; currentHighlightedRowId = null; var first = true; @@ -2762,8 +3458,8 @@ function populateBomBody() { // expand bom table expandedTable = [] for (var bomentry of bomtable) { - for (var ref of bomentry[3]) { - expandedTable.push([1, bomentry[1], bomentry[2], [ref], bomentry[4], bomentry[5]]); + for (var ref of bomentry) { + expandedTable.push([ref]); } } bomtable = expandedTable; @@ -2797,47 +3493,56 @@ function populateBomBody() { continue; } } else { - references = bomentry[3]; + references = bomentry; } - // Checkboxes - for (var checkbox of settings.checkboxes) { - if (checkbox) { - td = document.createElement("TD"); - var input = document.createElement("input"); - input.type = "checkbox"; - input.onchange = createCheckboxChangeHandler(checkbox, references, tr); - setBomCheckboxState(checkbox, input, references); - if (input.checked && settings.darkenWhenChecked == checkbox) { - tr.classList.add("checked"); + // Filter hidden columns + var columns = settings.columnOrder.filter(e => !settings.hiddenColumns.includes(e)); + columns.forEach((column) => { + if (column === placeholderColumn) { + var n = 1; + if (column === "checkboxes") + n = settings.checkboxes.length; + for (i = 0; i < n; i++) { + td = placeHolderElements.shift(); + tr.appendChild(td); } - td.appendChild(input); + return; + } else if (column === "checkboxes") { + for (var checkbox of settings.checkboxes) { + if (checkbox) { + td = document.createElement("TD"); + var input = document.createElement("input"); + input.type = "checkbox"; + input.onchange = createCheckboxChangeHandler(checkbox, references, tr); + setBomCheckboxState(checkbox, input, references); + if (input.checked && settings.markWhenChecked == checkbox) { + tr.classList.add("checked"); + } + td.appendChild(input); + tr.appendChild(td); + } + } + } else if (column === "References") { + td = document.createElement("TD"); + td.innerHTML = highlightFilter(references.map(r => r[0]).join(", ")); + tr.appendChild(td); + } else if (column === "Quantity" && settings.bommode == "grouped") { + // Quantity + td = document.createElement("TD"); + td.textContent = references.length; + tr.appendChild(td); + } else { + // All the other fields + var field_index = config.fields.indexOf(column) + if (field_index < 0) + return; + var valueSet = new Set(); + references.map(r => r[1]).forEach((id) => valueSet.add(pcbdata.bom.fields[id][field_index])); + td = document.createElement("TD"); + td.innerHTML = highlightFilter(Array.from(valueSet).join(", ")); tr.appendChild(td); } - } - // References - td = document.createElement("TD"); - td.innerHTML = highlightFilter(references.map(r => r[0]).join(", ")); - tr.appendChild(td); - // Extra fields - for (var i in config.extra_fields) { - td = document.createElement("TD"); - td.innerHTML = highlightFilter(bomentry[4][i]); - tr.appendChild(td); - } - // Value - td = document.createElement("TD"); - td.innerHTML = highlightFilter(bomentry[1]); - tr.appendChild(td); - // Footprint - td = document.createElement("TD"); - td.innerHTML = highlightFilter(bomentry[2]); - tr.appendChild(td); - if (settings.bommode == "grouped") { - // Quantity - td = document.createElement("TD"); - td.textContent = bomentry[3].length; - tr.appendChild(td); - } + }); } bom.appendChild(tr); var handler = createRowHighlightHandler(tr.id, references, netname); @@ -2848,7 +3553,7 @@ function populateBomBody() { }); if (references !== null) { for (var refIndex of references.map(r => r[1])) { - moduleIndexToHandler[refIndex] = handler; + footprintIndexToHandler[refIndex] = handler; } } if (netname !== null) { @@ -2860,13 +3565,12 @@ function populateBomBody() { } } EventHandler.emitEvent( - IBOM_EVENT_TYPES.BOM_BODY_CHANGE_EVENT, - { - filter: filter, - reflookup: reflookup, - checkboxes: settings.checkboxes, - bommode: settings.bommode, - }); + IBOM_EVENT_TYPES.BOM_BODY_CHANGE_EVENT, { + filter: filter, + reflookup: reflookup, + checkboxes: settings.checkboxes, + bommode: settings.bommode, + }); } function highlightPreviousRow() { @@ -2910,15 +3614,17 @@ function highlightNextRow() { function populateBomTable() { populateBomHeader(); populateBomBody(); + setBomHandlers(); + resizableGrid(bomhead); } -function modulesClicked(moduleIndexes) { - var lastClickedIndex = moduleIndexes.indexOf(lastClicked); - for (var i = 1; i <= moduleIndexes.length; i++) { - var refIndex = moduleIndexes[(lastClickedIndex + i) % moduleIndexes.length]; - if (refIndex in moduleIndexToHandler) { +function footprintsClicked(footprintIndexes) { + var lastClickedIndex = footprintIndexes.indexOf(lastClicked); + for (var i = 1; i <= footprintIndexes.length; i++) { + var refIndex = footprintIndexes[(lastClickedIndex + i) % footprintIndexes.length]; + if (refIndex in footprintIndexToHandler) { lastClicked = refIndex; - moduleIndexToHandler[refIndex](); + footprintIndexToHandler[refIndex](); smoothScrollToRow(currentHighlightedRowId); break; } @@ -2930,7 +3636,7 @@ function netClicked(net) { netsToHandler[net](); smoothScrollToRow(currentHighlightedRowId); } else { - clearHighlightedModules(); + clearHighlightedFootprints(); highlightedNet = net; drawHighlights(); } @@ -2984,10 +3690,14 @@ function populateMetadata() { document.title = pcbdata.metadata.title + " BOM"; } // Calculate board stats - var fp_f = 0, fp_b = 0, pads_f = 0, pads_b = 0, pads_th = 0; - for (var i = 0; i < pcbdata.modules.length; i++) { + var fp_f = 0, + fp_b = 0, + pads_f = 0, + pads_b = 0, + pads_th = 0; + for (var i = 0; i < pcbdata.footprints.length; i++) { if (pcbdata.bom.skipped.includes(i)) continue; - var mod = pcbdata.modules[i]; + var mod = pcbdata.footprints[i]; if (mod.layer == "F") { fp_f++; } else { @@ -3101,23 +3811,35 @@ function changeBomMode(mode) { document.getElementById("bom-grouped-btn").classList.remove("depressed"); document.getElementById("bom-ungrouped-btn").classList.remove("depressed"); document.getElementById("bom-netlist-btn").classList.remove("depressed"); + var chkbxs = document.getElementsByClassName("visibility_checkbox"); + switch (mode) { case 'grouped': document.getElementById("bom-grouped-btn").classList.add("depressed"); + for (var i = 0; i < chkbxs.length; i++) { + chkbxs[i].disabled = false; + } break; case 'ungrouped': document.getElementById("bom-ungrouped-btn").classList.add("depressed"); + for (var i = 0; i < chkbxs.length; i++) { + chkbxs[i].disabled = false; + } break; case 'netlist': document.getElementById("bom-netlist-btn").classList.add("depressed"); + for (var i = 0; i < chkbxs.length; i++) { + chkbxs[i].disabled = true; + } } + writeStorage("bommode", mode); if (mode != settings.bommode) { settings.bommode = mode; bomSortFunction = null; currentSortColumn = null; currentSortOrder = null; - clearHighlightedModules(); + clearHighlightedFootprints(); } populateBomTable(); } @@ -3159,16 +3881,21 @@ function checkBomCheckbox(bomrowid, checkboxname) { function setBomCheckboxes(value) { writeStorage("bomCheckboxes", value); - settings.checkboxes = value.split(",").filter((e) => e); + settings.checkboxes = value.split(",").map((e) => e.trim()).filter((e) => e); prepCheckboxes(); - populateBomTable(); - populateDarkenWhenCheckedOptions(); + populateMarkWhenCheckedOptions(); + setMarkWhenChecked(settings.markWhenChecked); } -function setDarkenWhenChecked(value) { - writeStorage("darkenWhenChecked", value); - settings.darkenWhenChecked = value; +function setMarkWhenChecked(value) { + writeStorage("markWhenChecked", value); + settings.markWhenChecked = value; + markedFootprints.clear(); + for (var ref of (value ? getStoredCheckboxRefs(value) : [])) { + markedFootprints.add(ref); + } populateBomTable(); + drawHighlights(); } function prepCheckboxes() { @@ -3200,8 +3927,8 @@ function prepCheckboxes() { } } -function populateDarkenWhenCheckedOptions() { - var container = document.getElementById("darkenWhenCheckedContainer"); +function populateMarkWhenCheckedOptions() { + var container = document.getElementById("markWhenCheckedContainer"); if (settings.checkboxes.length == 0) { container.parentElement.style.display = "none"; @@ -3212,21 +3939,21 @@ function populateDarkenWhenCheckedOptions() { container.parentElement.style.display = "inline-block"; function createOption(name, displayName) { - var id = "darkenWhenChecked-" + name; + var id = "markWhenChecked-" + name; var div = document.createElement("div"); div.classList.add("radio-container"); var input = document.createElement("input"); input.type = "radio"; - input.name = "darkenWhenChecked"; + input.name = "markWhenChecked"; input.value = name; input.id = id; - input.onchange = () => setDarkenWhenChecked(name); + input.onchange = () => setMarkWhenChecked(name); div.appendChild(input); // Preserve the selected element when the checkboxes change - if (name == settings.darkenWhenChecked) { + if (name == settings.markWhenChecked) { input.checked = true; } @@ -3245,14 +3972,14 @@ function populateDarkenWhenCheckedOptions() { function updateCheckboxStats(checkbox) { var checked = getStoredCheckboxRefs(checkbox).size; - var total = pcbdata.modules.length - pcbdata.bom.skipped.length; + var total = pcbdata.footprints.length - pcbdata.bom.skipped.length; var percent = checked * 100.0 / total; var td = document.getElementById("checkbox-stats-" + checkbox); td.firstChild.style.width = percent + "%"; td.lastChild.innerHTML = checked + "/" + total + " (" + Math.round(percent) + "%)"; } -document.onkeydown = function(e) { +document.onkeydown = function (e) { switch (e.key) { case "n": if (document.activeElement.type == "text") { @@ -3314,6 +4041,7 @@ document.onkeydown = function(e) { } if (e.key >= '1' && e.key <= '9') { toggleBomCheckbox(currentHighlightedRowId, parseInt(e.key)); + e.preventDefault(); } } } @@ -3324,7 +4052,7 @@ function hideNetlistButton() { document.getElementById("bom-netlist-btn").style.display = "none"; } -window.onload = function(e) { +window.onload = function (e) { initUtils(); initRender(); initStorage(); @@ -3340,7 +4068,7 @@ window.onload = function(e) { hideNetlistButton(); } initDone = true; - prepCheckboxes(); + setBomCheckboxes(document.getElementById("bomCheckboxes").value); // Triggers render changeBomLayout(settings.bomlayout); @@ -3431,8 +4159,8 @@ window.matchMedia("print").addListener(resizeAll); oninput="setBomCheckboxes(this.value)">