[PCB Variant] Implemented the 3D filtering
- The PCB filtering code was moved to make it, some errors could be introduced
This commit is contained in:
parent
b0b7c6c041
commit
4dc93da42b
|
|
@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- New outputs:
|
||||
- PCB_Variant: saves a PCB with filters and variants applied.
|
||||
- Support for Eurocircuits drill adjust to fix small OARs.
|
||||
Option `eurocircuits_reduce_holes`. (#227)
|
||||
|
||||
|
|
|
|||
31
README.md
31
README.md
|
|
@ -2270,6 +2270,34 @@ Notes:
|
|||
Internally we use 10 for low priority, 90 for high priority and 50 for most outputs.
|
||||
- `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested.
|
||||
|
||||
* PCB with variant generator
|
||||
* Type: `pcb_variant`
|
||||
* Description: Creates a copy of the PCB with all the filters and variants applied.
|
||||
This copy isn't intended for development.
|
||||
Is just a tweaked version of the original where you can look at the results.
|
||||
* Valid keys:
|
||||
- **`comment`**: [string=''] A comment for documentation purposes.
|
||||
- **`dir`**: [string='./'] Output directory for the generated files.
|
||||
If it starts with `+` the rest is concatenated to the default dir.
|
||||
- **`name`**: [string=''] Used to identify this particular output definition.
|
||||
- **`options`**: [dict] Options for the `pcb_variant` output.
|
||||
* Valid keys:
|
||||
- **`output`**: [string='%f-%i%I%v.%x'] Filename for the output (%i=variant, %x=kicad_pcb). Affected by global options.
|
||||
- `dnf_filter`: [string|list(string)='_none'] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
- `hide_excluded`: [boolean=false] Hide components in the Fab layer that are marked as excluded by a variant.
|
||||
- `variant`: [string=''] Board variant to apply.
|
||||
- `category`: [string|list(string)=''] The category for this output. If not specified an internally defined category is used.
|
||||
Categories looks like file system paths, i.e. PCB/fabrication/gerber.
|
||||
- `disable_run_by_default`: [string|boolean] Use it to disable the `run_by_default` status of other output.
|
||||
Useful when this output extends another and you don't want to generate the original.
|
||||
Use the boolean true value to disable the output you are extending.
|
||||
- `extends`: [string=''] Copy the `options` section from the indicated output.
|
||||
- `output_id`: [string=''] Text to use for the %I expansion content. To differentiate variations of this output.
|
||||
- `priority`: [number=50] [0,100] Priority for this output. High priority outputs are created first.
|
||||
Internally we use 10 for low priority, 90 for high priority and 50 for most outputs.
|
||||
- `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested.
|
||||
|
||||
* PcbDraw - Beautiful 2D PCB render
|
||||
* Type: `pcbdraw`
|
||||
* Description: Exports the PCB as a 2D model (SVG, PNG or JPG).
|
||||
|
|
@ -2741,6 +2769,9 @@ Notes:
|
|||
- `converted_output`: [string='%f-%i%I%v.%x'] Converted output file name (%i='report', %x=`convert_to`).
|
||||
Note that the extension should match the `convert_to` value. Affected by global options.
|
||||
- `eurocircuits_class_target`: [string='10F'] Which Eurocircuits class are we aiming at.
|
||||
- `eurocircuits_reduce_holes`: [number=0.45] When computing the Eurocircuits category: Final holes sizes smaller or equal to this given
|
||||
diameter can be reduced to accommodate the correct annular ring values.
|
||||
Use 0 to disable it.
|
||||
- `category`: [string|list(string)=''] The category for this output. If not specified an internally defined category is used.
|
||||
Categories looks like file system paths, i.e. PCB/fabrication/gerber.
|
||||
- `disable_run_by_default`: [string|boolean] Use it to disable the `run_by_default` status of other output.
|
||||
|
|
|
|||
|
|
@ -1194,6 +1194,23 @@ outputs:
|
|||
variant: ''
|
||||
# [string=''] Color used for through-hole `colored_vias`
|
||||
via_color: ''
|
||||
# PCB with variant generator:
|
||||
# This copy isn't intended for development.
|
||||
# Is just a tweaked version of the original where you can look at the results.
|
||||
- name: 'pcb_variant_example'
|
||||
comment: 'Creates a copy of the PCB with all the filters and variants applied.'
|
||||
type: 'pcb_variant'
|
||||
dir: 'Example/pcb_variant_dir'
|
||||
options:
|
||||
# [string|list(string)='_none'] Name of the filter to mark components as not fitted.
|
||||
# A short-cut to use for simple cases where a variant is an overkill
|
||||
dnf_filter: '_none'
|
||||
# [boolean=false] Hide components in the Fab layer that are marked as excluded by a variant
|
||||
hide_excluded: false
|
||||
# [string='%f-%i%I%v.%x'] Filename for the output (%i=variant, %x=kicad_pcb). Affected by global options
|
||||
output: '%f-%i%I%v.%x'
|
||||
# [string=''] Board variant to apply
|
||||
variant: ''
|
||||
# PcbDraw - Beautiful 2D PCB render:
|
||||
# Uses configurable colors.
|
||||
# Can also render the components if the 2D models are available
|
||||
|
|
@ -1634,6 +1651,10 @@ outputs:
|
|||
do_convert: false
|
||||
# [string='10F'] Which Eurocircuits class are we aiming at
|
||||
eurocircuits_class_target: '10F'
|
||||
# [number=0.45] When computing the Eurocircuits category: Final holes sizes smaller or equal to this given
|
||||
# diameter can be reduced to accommodate the correct annular ring values.
|
||||
# Use 0 to disable it
|
||||
eurocircuits_reduce_holes: 0.45
|
||||
# [string='%f-%i%I%v.%x'] Output file name (%i='report', %x='txt'). Affected by global options
|
||||
output: '%f-%i%I%v.%x'
|
||||
# [string='full'] Name for one of the internal templates (full, full_svg, simple) or a custom template file.
|
||||
|
|
|
|||
|
|
@ -243,6 +243,8 @@ SILK_COLORS = {'black': "0b1013", 'white': "d5dce4"}
|
|||
KICAD5_SVG_SCALE = 116930/297002200
|
||||
# Some browser name to pretend
|
||||
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0'
|
||||
# Text used to disable 3D models
|
||||
DISABLE_3D_MODEL_TEXT = '_Disabled_by_KiBot'
|
||||
|
||||
|
||||
class Rect(object):
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@
|
|||
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
import os
|
||||
from copy import deepcopy
|
||||
from tempfile import NamedTemporaryFile, mkdtemp
|
||||
from glob import glob
|
||||
import os
|
||||
import re
|
||||
from tempfile import NamedTemporaryFile, mkdtemp
|
||||
from .gs import GS
|
||||
from .kiplot import load_sch, get_board_comps_data
|
||||
from .misc import Rect, W_WRONGPASTE
|
||||
from .misc import Rect, W_WRONGPASTE, DISABLE_3D_MODEL_TEXT
|
||||
if not GS.kicad_version_n:
|
||||
# When running the regression tests we need it
|
||||
from kibot.__main__ import detect_kicad
|
||||
|
|
@ -197,6 +198,8 @@ class VariantOptions(BaseOptions):
|
|||
A short-cut to use for simple cases where a variant is an overkill """
|
||||
super().__init__()
|
||||
self._comps = None
|
||||
self.undo_3d_models = {}
|
||||
self.undo_3d_models_rep = {}
|
||||
|
||||
def config(self, parent):
|
||||
super().config(parent)
|
||||
|
|
@ -408,23 +411,199 @@ class VariantOptions(BaseOptions):
|
|||
for gi in self.old_bfab:
|
||||
gi.SetLayer(self.bfab)
|
||||
|
||||
def filter_pcb_components(self, board):
|
||||
def replace_3D_models(self, models, new_model, c):
|
||||
""" Changes the 3D model using a provided model.
|
||||
Stores changes in self.undo_3d_models_rep """
|
||||
logger.debug('Changing 3D models for '+c.ref)
|
||||
# Get the model references
|
||||
models_l = []
|
||||
while not models.empty():
|
||||
models_l.append(models.pop())
|
||||
# Check if we have more than one model
|
||||
c_models = len(models_l)
|
||||
if c_models > 1:
|
||||
new_model = new_model.split(',')
|
||||
c_replace = len(new_model)
|
||||
if c_models != c_replace:
|
||||
raise KiPlotConfigurationError('Found {} models in component {}, but {} replacements provided'.
|
||||
format(c_models, c, c_replace))
|
||||
else:
|
||||
new_model = [new_model]
|
||||
# Change the models
|
||||
replaced = []
|
||||
for i, m3d in enumerate(models_l):
|
||||
replaced.append(m3d.m_Filename)
|
||||
m3d.m_Filename = new_model[i]
|
||||
self.undo_3d_models_rep[c.ref] = replaced
|
||||
# Push the models back
|
||||
for model in models_l:
|
||||
models.push_front(model)
|
||||
|
||||
def undo_3d_models_rename(self, board):
|
||||
""" Restores the file name for any renamed 3D module """
|
||||
for m in GS.get_modules_board(board):
|
||||
# Get the model references
|
||||
models = m.Models()
|
||||
models_l = []
|
||||
while not models.empty():
|
||||
models_l.append(models.pop())
|
||||
# Fix any changed path
|
||||
replaced = self.undo_3d_models_rep.get(m.GetReference())
|
||||
for i, m3d in enumerate(models_l):
|
||||
if m3d.m_Filename in self.undo_3d_models:
|
||||
m3d.m_Filename = self.undo_3d_models[m3d.m_Filename]
|
||||
if replaced:
|
||||
m3d.m_Filename = replaced[i]
|
||||
# Push the models back
|
||||
for model in models_l:
|
||||
models.push_front(model)
|
||||
# Reset the list of changes
|
||||
self.undo_3d_models = {}
|
||||
self.undo_3d_models_rep = {}
|
||||
|
||||
def remove_3D_models(self, board, comps_hash):
|
||||
""" Removes 3D models for excluded or not fitted components.
|
||||
Applies the global_field_3D_model model rename """
|
||||
if not comps_hash:
|
||||
return
|
||||
# Remove the 3D models for not fitted components
|
||||
rem_models = []
|
||||
for m in GS.get_modules_board(board):
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c:
|
||||
# The filter/variant knows about this component
|
||||
models = m.Models()
|
||||
if c.included and not c.fitted:
|
||||
# Not fitted, remove the 3D model
|
||||
rem_m_models = []
|
||||
while not models.empty():
|
||||
rem_m_models.append(models.pop())
|
||||
rem_models.append(rem_m_models)
|
||||
else:
|
||||
# Fitted
|
||||
new_model = c.get_field_value(GS.global_field_3D_model)
|
||||
if new_model:
|
||||
# We will change the 3D model
|
||||
self.replace_3D_models(models, new_model, c)
|
||||
self.rem_models = rem_models
|
||||
|
||||
def restore_3D_models(self, board, comps_hash):
|
||||
""" Restore the removed 3D models.
|
||||
Restores the renamed models. """
|
||||
self.undo_3d_models_rename(board)
|
||||
if not comps_hash:
|
||||
return
|
||||
# Undo the removing
|
||||
for m in GS.get_modules_board(board):
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and c.included and not c.fitted:
|
||||
models = m.Models()
|
||||
restore = self.rem_models.pop(0)
|
||||
for model in restore:
|
||||
models.push_front(model)
|
||||
|
||||
def apply_list_of_3D_models(self, enable, slots, m, var):
|
||||
# Disable the unused models adding bogus text to the end
|
||||
slots = [int(v) for v in slots if v]
|
||||
models = m.Models()
|
||||
m_objs = []
|
||||
# Extract the models, we get a copy
|
||||
while not models.empty():
|
||||
m_objs.insert(0, models.pop())
|
||||
for i, m3d in enumerate(m_objs):
|
||||
if self.extra_debug:
|
||||
logger.debug('- {} {} {} {}'.format(var, i+1, i+1 in slots, m3d.m_Filename))
|
||||
if i+1 not in slots:
|
||||
if enable:
|
||||
# Revert the added text
|
||||
m3d.m_Filename = m3d.m_Filename[:-self.len_disable]
|
||||
else:
|
||||
# Not used, add text to make their name invalid
|
||||
m3d.m_Filename += DISABLE_3D_MODEL_TEXT
|
||||
# Push it back to the module
|
||||
models.push_back(m3d)
|
||||
|
||||
def apply_3D_variant_aspect(self, board, enable=False):
|
||||
""" Disable/Enable the 3D models that aren't for this variant.
|
||||
This mechanism uses the MTEXT attributes. """
|
||||
# The magic text is %variant:slot1,slot2...%
|
||||
field_regex = re.compile(r'\%([^:]+):([\d,]*)\%') # Generic (by name)
|
||||
field_regex_sp = re.compile(r'\$([^:]*):([\d,]*)\$') # Variant specific
|
||||
self.extra_debug = extra_debug = GS.debug_level > 3
|
||||
if extra_debug:
|
||||
logger.debug("{} 3D models that aren't for this variant".format('Enable' if enable else 'Disable'))
|
||||
self.len_disable = len(DISABLE_3D_MODEL_TEXT)
|
||||
variant_name = self.variant.name
|
||||
for m in GS.get_modules_board(board):
|
||||
if extra_debug:
|
||||
logger.debug("Processing module " + m.GetReference())
|
||||
default = None
|
||||
matched = False
|
||||
# Look for text objects
|
||||
for gi in m.GraphicalItems():
|
||||
if gi.GetClass() == 'MTEXT':
|
||||
# Check if the text matches the magic style
|
||||
text = gi.GetText().strip()
|
||||
match = field_regex.match(text)
|
||||
if match:
|
||||
# Check if this is for the current variant
|
||||
var = match.group(1)
|
||||
slots = match.group(2).split(',') if match.group(2) else []
|
||||
# Do the match
|
||||
if var == '_default_':
|
||||
default = slots
|
||||
if self.extra_debug:
|
||||
logger.debug('- Found defaults: {}'.format(slots))
|
||||
else:
|
||||
matched = var == variant_name
|
||||
if matched:
|
||||
self.apply_list_of_3D_models(enable, slots, m, var)
|
||||
break
|
||||
else:
|
||||
# Try with the variant specific pattern
|
||||
match = field_regex_sp.match(text)
|
||||
if match:
|
||||
var = match.group(1)
|
||||
slots = match.group(2).split(',') if match.group(2) else []
|
||||
# Do the match
|
||||
matched = self.variant.matches_variant(var)
|
||||
if matched:
|
||||
self.apply_list_of_3D_models(enable, slots, m, var)
|
||||
break
|
||||
if not matched and default is not None:
|
||||
self.apply_list_of_3D_models(enable, slots, m, '_default_')
|
||||
|
||||
def filter_pcb_components(self, board, do_3D=False, do_2D=True):
|
||||
if not self._comps:
|
||||
return False
|
||||
self.comps_hash = self.get_refs_hash()
|
||||
self.cross_modules(board, self.comps_hash)
|
||||
self.remove_paste_and_glue(board, self.comps_hash)
|
||||
if hasattr(self, 'hide_excluded') and self.hide_excluded:
|
||||
self.remove_fab(board, self.comps_hash)
|
||||
if do_2D:
|
||||
self.cross_modules(board, self.comps_hash)
|
||||
self.remove_paste_and_glue(board, self.comps_hash)
|
||||
if hasattr(self, 'hide_excluded') and self.hide_excluded:
|
||||
self.remove_fab(board, self.comps_hash)
|
||||
if do_3D:
|
||||
# Disable the models that aren't for this variant
|
||||
self.apply_3D_variant_aspect(board)
|
||||
# Remove the 3D models for not fitted components (also rename)
|
||||
self.remove_3D_models(board, self.comps_hash)
|
||||
return True
|
||||
|
||||
def unfilter_pcb_components(self, board):
|
||||
def unfilter_pcb_components(self, board, do_3D=False, do_2D=True):
|
||||
if not self._comps:
|
||||
return
|
||||
self.uncross_modules(board, self.comps_hash)
|
||||
self.restore_paste_and_glue(board, self.comps_hash)
|
||||
if hasattr(self, 'hide_excluded') and self.hide_excluded:
|
||||
self.restore_fab(board, self.comps_hash)
|
||||
if do_2D:
|
||||
self.uncross_modules(board, self.comps_hash)
|
||||
self.restore_paste_and_glue(board, self.comps_hash)
|
||||
if hasattr(self, 'hide_excluded') and self.hide_excluded:
|
||||
self.restore_fab(board, self.comps_hash)
|
||||
if do_3D:
|
||||
# Undo the removing (also rename)
|
||||
self.restore_3D_models(board, self.comps_hash)
|
||||
# Re-enable the modules that aren't for this variant
|
||||
self.apply_3D_variant_aspect(board, enable=True)
|
||||
|
||||
def set_title(self, title):
|
||||
self.old_title = None
|
||||
|
|
|
|||
|
|
@ -4,19 +4,16 @@
|
|||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
import tempfile
|
||||
from .error import KiPlotConfigurationError
|
||||
from .misc import W_MISS3D, W_FAILDL
|
||||
from .gs import (GS)
|
||||
from .misc import W_MISS3D, W_FAILDL, DISABLE_3D_MODEL_TEXT
|
||||
from .gs import GS
|
||||
from .out_base import VariantOptions, BaseOutput
|
||||
from .kicad.config import KiConf
|
||||
from .macros import macros, document # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger()
|
||||
DISABLE_TEXT = '_Disabled_by_KiBot'
|
||||
|
||||
|
||||
class Base3DOptions(VariantOptions):
|
||||
|
|
@ -49,56 +46,11 @@ class Base3DOptions(VariantOptions):
|
|||
f.write(r.content)
|
||||
return dest
|
||||
|
||||
def undo_3d_models_rename(self):
|
||||
""" Restores the file name for any renamed 3D module """
|
||||
for m in GS.get_modules():
|
||||
# Get the model references
|
||||
models = m.Models()
|
||||
models_l = []
|
||||
while not models.empty():
|
||||
models_l.append(models.pop())
|
||||
# Fix any changed path
|
||||
replaced = self.undo_3d_models_rep.get(m.GetReference())
|
||||
for i, m3d in enumerate(models_l):
|
||||
if m3d.m_Filename in self.undo_3d_models:
|
||||
m3d.m_Filename = self.undo_3d_models[m3d.m_Filename]
|
||||
if replaced:
|
||||
m3d.m_Filename = replaced[i]
|
||||
# Push the models back
|
||||
for model in models_l:
|
||||
models.push_front(model)
|
||||
|
||||
def replace_models(self, models, new_model, c):
|
||||
""" Changes the 3D model using a provided model """
|
||||
logger.debug('Changing 3D models for '+c.ref)
|
||||
# Get the model references
|
||||
models_l = []
|
||||
while not models.empty():
|
||||
models_l.append(models.pop())
|
||||
# Check if we have more than one model
|
||||
c_models = len(models_l)
|
||||
if c_models > 1:
|
||||
new_model = new_model.split(',')
|
||||
c_replace = len(new_model)
|
||||
if c_models != c_replace:
|
||||
raise KiPlotConfigurationError('Found {} models in component {}, but {} replacements provided'.
|
||||
format(c_models, c, c_replace))
|
||||
else:
|
||||
new_model = [new_model]
|
||||
# Change the models
|
||||
replaced = []
|
||||
for i, m3d in enumerate(models_l):
|
||||
replaced.append(m3d.m_Filename)
|
||||
m3d.m_Filename = new_model[i]
|
||||
self.undo_3d_models_rep[c.ref] = replaced
|
||||
# Push the models back
|
||||
for model in models_l:
|
||||
models.push_front(model)
|
||||
|
||||
def download_models(self):
|
||||
""" Check we have the 3D models.
|
||||
Inform missing models.
|
||||
Try to download the missing models """
|
||||
Try to download the missing models
|
||||
Stores changes in self.undo_3d_models_rep """
|
||||
models_replaced = False
|
||||
# Load KiCad configuration so we can expand the 3D models path
|
||||
KiConf.init(GS.pcb_file)
|
||||
|
|
@ -116,7 +68,7 @@ class Base3DOptions(VariantOptions):
|
|||
models_l.append(models.pop())
|
||||
# Look for all the 3D models for this footprint
|
||||
for m3d in models_l:
|
||||
if m3d.m_Filename.endswith(DISABLE_TEXT):
|
||||
if m3d.m_Filename.endswith(DISABLE_3D_MODEL_TEXT):
|
||||
# Skip models we intentionally disabled using a bogus name
|
||||
if extra_debug:
|
||||
logger.debug("- Skipping {} (disabled)".format(m3d.m_Filename))
|
||||
|
|
@ -180,77 +132,6 @@ class Base3DOptions(VariantOptions):
|
|||
models.add(full_name)
|
||||
return list(models)
|
||||
|
||||
def apply_list_of_models(self, enable, slots, m, var):
|
||||
# Disable the unused models adding bogus text to the end
|
||||
slots = [int(v) for v in slots if v]
|
||||
models = m.Models()
|
||||
m_objs = []
|
||||
# Extract the models, we get a copy
|
||||
while not models.empty():
|
||||
m_objs.insert(0, models.pop())
|
||||
for i, m3d in enumerate(m_objs):
|
||||
if self.extra_debug:
|
||||
logger.debug('- {} {} {} {}'.format(var, i+1, i+1 in slots, m3d.m_Filename))
|
||||
if i+1 not in slots:
|
||||
if enable:
|
||||
# Revert the added text
|
||||
m3d.m_Filename = m3d.m_Filename[:-self.len_disable]
|
||||
else:
|
||||
# Not used, add text to make their name invalid
|
||||
m3d.m_Filename += DISABLE_TEXT
|
||||
# Push it back to the module
|
||||
models.push_back(m3d)
|
||||
|
||||
def apply_variant_aspect(self, enable=False):
|
||||
""" Disable/Enable the 3D models that aren't for this variant.
|
||||
This mechanism uses the MTEXT attributes. """
|
||||
# The magic text is %variant:slot1,slot2...%
|
||||
field_regex = re.compile(r'\%([^:]+):([\d,]*)\%') # Generic (by name)
|
||||
field_regex_sp = re.compile(r'\$([^:]*):([\d,]*)\$') # Variant specific
|
||||
self.extra_debug = extra_debug = GS.debug_level > 3
|
||||
if extra_debug:
|
||||
logger.debug("{} 3D models that aren't for this variant".format('Enable' if enable else 'Disable'))
|
||||
self.len_disable = len(DISABLE_TEXT)
|
||||
variant_name = self.variant.name
|
||||
for m in GS.get_modules():
|
||||
if extra_debug:
|
||||
logger.debug("Processing module " + m.GetReference())
|
||||
default = None
|
||||
matched = False
|
||||
# Look for text objects
|
||||
for gi in m.GraphicalItems():
|
||||
if gi.GetClass() == 'MTEXT':
|
||||
# Check if the text matches the magic style
|
||||
text = gi.GetText().strip()
|
||||
match = field_regex.match(text)
|
||||
if match:
|
||||
# Check if this is for the current variant
|
||||
var = match.group(1)
|
||||
slots = match.group(2).split(',') if match.group(2) else []
|
||||
# Do the match
|
||||
if var == '_default_':
|
||||
default = slots
|
||||
if self.extra_debug:
|
||||
logger.debug('- Found defaults: {}'.format(slots))
|
||||
else:
|
||||
matched = var == variant_name
|
||||
if matched:
|
||||
self.apply_list_of_models(enable, slots, m, var)
|
||||
break
|
||||
else:
|
||||
# Try with the variant specific pattern
|
||||
match = field_regex_sp.match(text)
|
||||
if match:
|
||||
var = match.group(1)
|
||||
slots = match.group(2).split(',') if match.group(2) else []
|
||||
# Do the match
|
||||
matched = self.variant.matches_variant(var)
|
||||
if matched:
|
||||
self.apply_list_of_models(enable, slots, m, var)
|
||||
break
|
||||
if not matched and default is not None:
|
||||
self.apply_list_of_models(enable, slots, m, '_default_')
|
||||
|
||||
def filter_components(self):
|
||||
self.undo_3d_models_rep = {}
|
||||
if not self._comps:
|
||||
|
|
@ -259,47 +140,14 @@ class Base3DOptions(VariantOptions):
|
|||
# Some missing components found and we downloaded them
|
||||
# Save the fixed board
|
||||
ret = self.save_tmp_board()
|
||||
# Undo the changes
|
||||
self.undo_3d_models_rename()
|
||||
# Undo the changes done during download
|
||||
self.undo_3d_models_rename(GS.board)
|
||||
return ret
|
||||
return GS.pcb_file
|
||||
comps_hash = self.get_refs_hash()
|
||||
# Disable the models that aren't for this variant
|
||||
self.apply_variant_aspect()
|
||||
# Remove the 3D models for not fitted components
|
||||
rem_models = []
|
||||
for m in GS.get_modules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c:
|
||||
# The filter/variant knows about this component
|
||||
models = m.Models()
|
||||
if c.included and not c.fitted:
|
||||
# Not fitted, remove the 3D model
|
||||
rem_m_models = []
|
||||
while not models.empty():
|
||||
rem_m_models.append(models.pop())
|
||||
rem_models.append(rem_m_models)
|
||||
else:
|
||||
# Fitted
|
||||
new_model = c.get_field_value(GS.global_field_3D_model)
|
||||
if new_model:
|
||||
# We will change the 3D model
|
||||
self.replace_models(models, new_model, c)
|
||||
self.filter_pcb_components(GS.board, do_3D=True, do_2D=False)
|
||||
self.download_models()
|
||||
fname = self.save_tmp_board()
|
||||
self.undo_3d_models_rename()
|
||||
# Undo the removing
|
||||
for m in GS.get_modules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if c and c.included and not c.fitted:
|
||||
models = m.Models()
|
||||
restore = rem_models.pop(0)
|
||||
for model in restore:
|
||||
models.push_front(model)
|
||||
# Re-enable the modules that aren't for this variant
|
||||
self.apply_variant_aspect(enable=True)
|
||||
self.unfilter_pcb_components(GS.board, do_3D=True, do_2D=False)
|
||||
return fname
|
||||
|
||||
def get_targets(self, out_dir):
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ class PCB_Variant_Options(VariantOptions):
|
|||
|
||||
def run(self, output):
|
||||
super().run(output)
|
||||
self.filter_pcb_components(GS.board)
|
||||
logger.error('Saving PCB to '+output)
|
||||
self.filter_pcb_components(GS.board, do_3D=True)
|
||||
logger.debug('Saving PCB to '+output)
|
||||
GS.board.Save(output)
|
||||
GS.copy_project(output)
|
||||
self.unfilter_pcb_components(GS.board)
|
||||
self.unfilter_pcb_components(GS.board, do_3D=True)
|
||||
|
||||
|
||||
@output_class
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ Outer Annular Ring: ${oar_mm} mm (${oar_mils} mils)
|
|||
- By design rules: ${oar_d_mm} mm (${oar_d_mils} mils)
|
||||
|
||||
Eurocircuits class: ${pattern_class}${drill_class}
|
||||
- Using min drill ${drill_real_ec_mm} mm for an OAR of ${oar_ec_mm} mm
|
||||
|
||||
|
||||
# General stats
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Example KiBot config file
|
||||
kibot:
|
||||
version: 1
|
||||
|
||||
variants:
|
||||
- name: 'default'
|
||||
comment: 'Default variant'
|
||||
type: ibom
|
||||
variants_blacklist: T2,T3
|
||||
|
||||
outputs:
|
||||
- name: 'pcb_default'
|
||||
comment: "PCB w/variant"
|
||||
type: pcb_variant
|
||||
options:
|
||||
variant: default
|
||||
scaling: 0
|
||||
title: 'Hello %V'
|
||||
Loading…
Reference in New Issue