Skip to content

Commit

Permalink
Make compression optional, limit fp precision
Browse files Browse the repository at this point in the history
All floating points are limited to 6 decimal digits, angles are limited
to 2 decimal digits.

This should reduce output file size quite a bit.

Fixes #226
  • Loading branch information
qu1ck committed Mar 5, 2021
1 parent 034cde1 commit 53aea0c
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 117 deletions.
9 changes: 9 additions & 0 deletions InteractiveHtmlBom/core/config.py
Expand Up @@ -54,6 +54,7 @@ class Config:
checkboxes = ','.join(default_checkboxes)
bom_view = bom_view_choices[1]
layer_view = layer_view_choices[1]
compression = True
open_browser = True

# General section
Expand Down Expand Up @@ -107,6 +108,7 @@ def load_from_ini(self):
self.checkboxes = f.Read('checkboxes', self.checkboxes)
self.bom_view = f.Read('bom_view', self.bom_view)
self.layer_view = f.Read('layer_view', self.layer_view)
self.compression = f.ReadBool('compression', self.compression)
self.open_browser = f.ReadBool('open_browser', self.open_browser)

f.SetPath('/general')
Expand Down Expand Up @@ -155,6 +157,7 @@ def save(self):
f.Write('checkboxes', self.checkboxes)
f.Write('bom_view', self.bom_view)
f.Write('layer_view', self.layer_view)
f.WriteBool('compression', self.compression)
f.WriteBool('open_browser', self.open_browser)

f.SetPath('/general')
Expand Down Expand Up @@ -198,6 +201,7 @@ def set_from_dialog(self, dlg):
self.bom_view = self.bom_view_choices[dlg.html.bomDefaultView.Selection]
self.layer_view = self.layer_view_choices[
dlg.html.layerDefaultView.Selection]
self.compression = dlg.html.compressionCheckbox.IsChecked()
self.open_browser = dlg.html.openBrowserCheckbox.IsChecked()

# General
Expand Down Expand Up @@ -242,6 +246,7 @@ def transfer_to_dialog(self, dlg):
self.bom_view)
dlg.html.layerDefaultView.Selection = self.layer_view_choices.index(
self.layer_view)
dlg.html.compressionCheckbox.Value = self.compression
dlg.html.openBrowserCheckbox.Value = self.open_browser

# General
Expand Down Expand Up @@ -321,6 +326,9 @@ def add_options(self, parser, file_name_format_hint):
parser.add_argument('--layer-view', default=self.layer_view,
choices=self.layer_view_choices,
help='Default layer view.')
parser.add_argument('--no-compression',
help='Disable compression of pcb data.',
action='store_true')
parser.add_argument('--no-browser', help='Do not launch browser.',
action='store_true')

Expand Down Expand Up @@ -388,6 +396,7 @@ def set_from_args(self, args):
self.checkboxes = args.checkboxes
self.bom_view = args.bom_view
self.layer_view = args.layer_view
self.compression = not args.no_compression
self.open_browser = not args.no_browser

# General
Expand Down
42 changes: 31 additions & 11 deletions InteractiveHtmlBom/core/ibom.py
Expand Up @@ -200,13 +200,28 @@ def process_substitutions(bom_name_format, pcb_file_name, metadata):
return name + '.html'


def get_compressed_pcbdata(pcbdata):
def round_floats(o, precision):
if isinstance(o, float):
return round(o, precision)
if isinstance(o, dict):
return {k: round_floats(v, precision) for k, v in o.items()}
if isinstance(o, (list, tuple)):
return [round_floats(x, precision) for x in o]
return o


def get_pcbdata_javascript(pcbdata, compression):
from .lzstring import LZString

pcbdata_js = LZString().compress_to_base64(json.dumps(pcbdata))
pcbdata_js = json.dumps(pcbdata_js)
js = "var pcbdata = JSON.parse(LZString.decompressFromBase64({}))"
return js.format(pcbdata_js)
js = "var pcbdata = {}"
pcbdata_str = json.dumps(round_floats(pcbdata, 6))

if compression:
log.info("Compressing pcb data")
pcbdata_str = json.dumps(LZString().compress_to_base64(pcbdata_str))
js = "var pcbdata = JSON.parse(LZString.decompressFromBase64({}))"

return js.format(pcbdata_str)


def generate_file(pcb_file_dir, pcb_file_name, pcbdata, config):
Expand All @@ -227,28 +242,33 @@ def get_file_content(file_name):
bom_file_dir = os.path.dirname(bom_file_name)
if not os.path.isdir(bom_file_dir):
os.makedirs(bom_file_dir)
log.info("Compressing pcb data")
compressed_pcbdata = get_compressed_pcbdata(pcbdata)
pcbdata_js = get_pcbdata_javascript(pcbdata, config.compression)
log.info("Dumping pcb data")
config_js = "var config = " + config.get_html_config()
html = get_file_content("ibom.html")
html = html.replace('///CSS///', get_file_content('ibom.css'))
html = html.replace('///USERCSS///', get_file_content('user.css'))
html = html.replace('///SPLITJS///', get_file_content('split.js'))
html = html.replace('///LZ-STRING///', get_file_content('lz-string.js'))
html = html.replace('///LZ-STRING///',
get_file_content('lz-string.js')
if config.compression else '')
html = html.replace('///POINTER_EVENTS_POLYFILL///',
get_file_content('pep.js'))
html = html.replace('///CONFIG///', config_js)
html = html.replace('///PCBDATA///', compressed_pcbdata)
html = html.replace('///UTILJS///', get_file_content('util.js'))
html = html.replace('///RENDERJS///', get_file_content('render.js'))
html = html.replace('///IBOMJS///', get_file_content('ibom.js'))
html = html.replace('///USERJS///', get_file_content('user.js'))
html = html.replace('///USERHEADER///', get_file_content('userheader.html'))
html = html.replace('///USERFOOTER///', get_file_content('userfooter.html'))
html = html.replace('///USERHEADER///',
get_file_content('userheader.html'))
html = html.replace('///USERFOOTER///',
get_file_content('userfooter.html'))
# Replace pcbdata last for better performance.
html = html.replace('///PCBDATA///', pcbdata_js)

with io.open(bom_file_name, 'wt', encoding='utf-8') as bom:
bom.write(html)

log.info("Created file %s", bom_file_name)
return bom_file_name

Expand Down
13 changes: 11 additions & 2 deletions InteractiveHtmlBom/dialog/dialog_base.py
Expand Up @@ -165,9 +165,18 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.
self.layerDefaultView.SetSelection( 1 )
b_sizer.Add( self.layerDefaultView, 0, wx.ALL|wx.EXPAND, 5 )

self.openBrowserCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Open browser", wx.DefaultPosition, wx.DefaultSize, 0 )
sbSizer10 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Miscellaneous" ), wx.VERTICAL )

self.compressionCheckbox = wx.CheckBox( sbSizer10.GetStaticBox(), wx.ID_ANY, u"Enable compression", wx.DefaultPosition, wx.DefaultSize, 0 )
self.compressionCheckbox.SetValue(True)
sbSizer10.Add( self.compressionCheckbox, 0, wx.ALL, 5 )

self.openBrowserCheckbox = wx.CheckBox( sbSizer10.GetStaticBox(), wx.ID_ANY, u"Open browser", wx.DefaultPosition, wx.DefaultSize, 0 )
self.openBrowserCheckbox.SetValue(True)
b_sizer.Add( self.openBrowserCheckbox, 0, wx.ALL, 5 )
sbSizer10.Add( self.openBrowserCheckbox, 0, wx.ALL, 5 )


b_sizer.Add( sbSizer10, 1, wx.EXPAND, 5 )


self.SetSizer( b_sizer )
Expand Down
8 changes: 4 additions & 4 deletions InteractiveHtmlBom/ecad/kicad.py
Expand Up @@ -73,8 +73,8 @@ def parse_shape(self, d):
"width": d.GetWidth() * 1e-6
}
if shape == "arc":
a1 = d.GetArcAngleStart() * 0.1
a2 = (d.GetArcAngleStart() + d.GetAngle()) * 0.1
a1 = round(d.GetArcAngleStart() * 0.1, 2)
a2 = round((d.GetArcAngleStart() + d.GetAngle()) * 0.1, 2)
if d.GetAngle() < 0:
(a1, a2) = (a2, a1)
return {
Expand Down Expand Up @@ -408,8 +408,8 @@ def parse_tracks(self, tracks):
else:
if track.GetLayer() in [pcbnew.F_Cu, pcbnew.B_Cu]:
if track.GetClass() == "ARC":
a1 = track.GetArcAngleStart() * 0.1
a2 = (track.GetArcAngleStart() + track.GetAngle()) * 0.1
a1 = round(track.GetArcAngleStart() * 0.1, 2)
a2 = round((track.GetArcAngleStart() + track.GetAngle()) * 0.1, 2)
if track.GetAngle() < 0:
(a1, a2) = (a2, a1)
track_dict = {
Expand Down
7 changes: 5 additions & 2 deletions InteractiveHtmlBom/ecad/svgpath.py
Expand Up @@ -512,10 +512,13 @@ def parse_path(pathdef, logger, current_pos=0j):
def create_path(lines):
"""Returns a path d-string."""

def limit_digits(val):
return format(val, '.6f').rstrip('0').replace(',', '.').rstrip('.')

parts = []
for line in lines:
parts.append('M{},{}'.format(line[0][0], line[0][1]))
parts.append('M{},{}'.format(*map(limit_digits, line[0])))
for point in line[1:]:
parts.append('L{},{}'.format(point[0], point[1]))
parts.append('L{},{}'.format(*map(limit_digits, point)))

return ''.join(parts)

0 comments on commit 53aea0c

Please sign in to comment.