diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6d4e9f..b57c5656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `skip_if_no_field` and `invert` options to the regex used in the generic filter. - Basic KiCost support. +- Experimental mechanism to change 3D models according to the variant. ### Changed - Errors and warnings from KiAuto now are printed as errors and warnings. diff --git a/README.md b/README.md index c06e33b9..b92acabd 100644 --- a/README.md +++ b/README.md @@ -369,6 +369,7 @@ Currently the only type available is `generic`. This filter implements the VARIANT:FIELD=VALUE renamer to get FIELD=VALUE when VARIANT is in use. * Valid keys: - `comment`: [string=''] A comment for documentation purposes. + - `force_variant`: [string=''] Use this variant instead of the current variant. Usefull for IBoM variants. - `name`: [string=''] Used to identify this particular filter definition. - `separator`: [string=':'] Separator used between the variant and the field name. - `variant_to_value`: [boolean=false] Rename fields matching the variant to the value of the component. diff --git a/kibot/fil_var_rename.py b/kibot/fil_var_rename.py index fbe04f40..a4455d9b 100644 --- a/kibot/fil_var_rename.py +++ b/kibot/fil_var_rename.py @@ -25,6 +25,8 @@ class Var_Rename(BaseFilter): # noqa: F821 """ Separator used between the variant and the field name """ self.variant_to_value = False """ Rename fields matching the variant to the value of the component """ + self.force_variant = '' + """ Use this variant instead of the current variant. Usefull for IBoM variants """ def config(self, parent): super().config(parent) @@ -33,10 +35,14 @@ class Var_Rename(BaseFilter): # noqa: F821 def filter(self, comp): """ Look for fields containing VARIANT:FIELD used to change fields according to the variant """ - if not GS.variant: - # No variant in use, nothing to do - return - for variant in GS.variant: + if self.force_variant: + variants = [self.force_variant] + else: + variants = GS.variant + if not variants: + # No variant in use, nothing to do + return + for variant in variants: for name, value in comp.get_user_fields(): res = name.strip().split(self.separator) if len(res) == 2: diff --git a/kibot/gs.py b/kibot/gs.py index 8c5fc94b..4bcd45a6 100644 --- a/kibot/gs.py +++ b/kibot/gs.py @@ -74,6 +74,7 @@ class GS(object): global_kiauto_wait_start = None global_kiauto_time_out_scale = None global_opts_class = None + global_3D_model_field = '_3D_model' test_boolean = True @staticmethod diff --git a/kibot/out_step.py b/kibot/out_step.py index faba05ec..7ada17de 100644 --- a/kibot/out_step.py +++ b/kibot/out_step.py @@ -81,13 +81,43 @@ class STEPOptions(VariantOptions): while not models.empty(): models_l.append(models.pop()) # Fix any changed path - for m3d in models_l: + 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. @@ -165,6 +195,7 @@ class STEPOptions(VariantOptions): def filter_components(self, dir): if not self._comps: + # No variant/filter to apply if self.download_models(): # Some missing components found and we downloaded them # Save the fixed board @@ -176,15 +207,25 @@ class STEPOptions(VariantOptions): comps_hash = self.get_refs_hash() # Remove the 3D models for not fitted components rem_models = [] + self.undo_3d_models_rep = {} for m in GS.board.GetModules(): ref = m.GetReference() c = comps_hash.get(ref, None) - if c and c.included and not c.fitted: + if c: + # The filter/variant knows about this component models = m.Models() - rem_m_models = [] - while not models.empty(): - rem_m_models.append(models.pop()) - rem_models.append(rem_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_3D_model_field) + if new_model: + # We will change the 3D model + self.replace_models(models, new_model, c) self.download_models() fname = self.save_board(dir) self.undo_3d_models_rename() diff --git a/tests/board_samples/kicad_5/kibom-variant_3.sch b/tests/board_samples/kicad_5/kibom-variant_3.sch index 6a3cd0d0..4c4a8afb 100644 --- a/tests/board_samples/kicad_5/kibom-variant_3.sch +++ b/tests/board_samples/kicad_5/kibom-variant_3.sch @@ -60,6 +60,7 @@ F 1 "1000" H 2570 1655 50 0000 L CNN F 2 "Resistor_SMD:R_0805_2012Metric" V 2430 1700 50 0001 C CNN F 3 "~" H 2500 1700 50 0001 C CNN F 4 "T1" H 2500 1700 50 0001 C CNN "Config" +F 5 "${KISYS3DMOD}/Resistor_SMD.3dshapes/R_2010_5025Metric.wrl" H 2500 1700 50 0001 C CNN "default:_3D_model" 1 2500 1700 1 0 0 -1 $EndComp diff --git a/tests/test_plot/test_step.py b/tests/test_plot/test_step.py index 1ac86808..7fc7c42d 100644 --- a/tests/test_plot/test_step.py +++ b/tests/test_plot/test_step.py @@ -57,7 +57,7 @@ def test_step_3(test_dir): def test_step_variant_1(test_dir): prj = 'kibom-variant_3' ctx = context.TestContext(test_dir, 'test_step_variant_1', prj, 'step_variant_1', '') - ctx.run() + ctx.run(extra_debug=True) # Check all outputs are there ctx.expect_out_file(prj+'-3D.step') ctx.clean_up(keep_project=True) diff --git a/tests/yaml_samples/step_variant_1.kibot.yaml b/tests/yaml_samples/step_variant_1.kibot.yaml index 9c9a0da4..0965ae90 100644 --- a/tests/yaml_samples/step_variant_1.kibot.yaml +++ b/tests/yaml_samples/step_variant_1.kibot.yaml @@ -2,11 +2,18 @@ kibot: version: 1 +filters: + - name: '3D change' + comment: 'Changes R2 3D model' + type: var_rename + force_variant: 'default' + variants: - name: 'default' comment: 'Default variant' type: ibom variants_blacklist: T2,T3 + pre_transform: '3D change' outputs: - name: 'step_default'