Made the 3D model selection more versatile

- Now you can choose between using the variant name or the variant
  specific mechanism.
- Works for all variants and both mechanism are simple.
This commit is contained in:
Salvador E. Tropea 2022-04-20 19:42:51 -03:00
parent de469577d1
commit ebf529fa0b
6 changed files with 64 additions and 35 deletions

View File

@ -714,18 +714,25 @@ Here are both variants:
![Top variant](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/samples/3D_Model_LCD/output/lcd-3D_top_variant_top.png)
If you preffer to use the variant specific matching mechanism you can use the following syntax:
```
$TEXT_TO_MATCH:SLOT1,SLOT2,SLOTN$
```
In this case the variant will be applied to the `TEXT_TO_MATCH`, if it matches (equivalent to a component fitted) the `SLOT` will be used.
Some important notes:
- If you want to control what models are used when no variant is used you'll need to create a `default` variant.
This is what the above example does. In this case the `default` variant shows all the connectors, but no display.
Note that changing the 3D model needs the variants infrastructure.
- If you are using variants and a lot of them select the same slots you can add special text: `%_default_:SLOTS%`.
- If you are using variants and a lot of them select the same slots you can add a special text: `%_default_:SLOTS%`.
This will be used if none %VARIANT_NAME:SLOT%` matched.
- If you want to disable a model and avoid any kind of warning add `_Disabled_by_KiBot` to the 3D model path.
This could be needed if you want to remove some model and you don't want to adjust the slot numbers.
- This mechanism can be used with any of the available variants. For this reason we use the `VARIANT_NAME` and we
avoid relying on any variant specific mechanism.
- If you are using KiCost variants and you want to match the regex against the text you can use the following syntax:
`%_kicost.TEXT:SLOTS%`.
avoid relying on any variant specific mechanism. But you can use the alternative syntax if you preffer the variant
specific matching system.
#### DNF and DNC internal keys

View File

@ -449,18 +449,25 @@ Here are both variants:
![Top variant](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/samples/3D_Model_LCD/output/lcd-3D_top_variant_top.png)
If you preffer to use the variant specific matching mechanism you can use the following syntax:
```
$TEXT_TO_MATCH:SLOT1,SLOT2,SLOTN$
```
In this case the variant will be applied to the `TEXT_TO_MATCH`, if it matches (equivalent to a component fitted) the `SLOT` will be used.
Some important notes:
- If you want to control what models are used when no variant is used you'll need to create a `default` variant.
This is what the above example does. In this case the `default` variant shows all the connectors, but no display.
Note that changing the 3D model needs the variants infrastructure.
- If you are using variants and a lot of them select the same slots you can add special text: `%_default_:SLOTS%`.
- If you are using variants and a lot of them select the same slots you can add a special text: `%_default_:SLOTS%`.
This will be used if none %VARIANT_NAME:SLOT%` matched.
- If you want to disable a model and avoid any kind of warning add `_Disabled_by_KiBot` to the 3D model path.
This could be needed if you want to remove some model and you don't want to adjust the slot numbers.
- This mechanism can be used with any of the available variants. For this reason we use the `VARIANT_NAME` and we
avoid relying on any variant specific mechanism.
- If you are using KiCost variants and you want to match the regex against the text you can use the following syntax:
`%_kicost.TEXT:SLOTS%`.
avoid relying on any variant specific mechanism. But you can use the alternative syntax if you preffer the variant
specific matching system.
#### DNF and DNC internal keys

View File

@ -205,15 +205,13 @@ class Base3DOptions(VariantOptions):
""" 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'\%([^:]+):(.*)\%')
field_regex = re.compile(r'\%([^:]+):(.*)\%') # Generic (by name)
field_regex_sp = re.compile(r'\$([^:]*):(.*)\$') # 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
var_re = None
if self.variant.type == 'kicost' and self.variant.variant:
var_re = re.compile(self.variant.variant, flags=re.IGNORECASE)
for m in GS.get_modules():
if extra_debug:
logger.debug("Processing module " + m.GetReference())
@ -230,10 +228,7 @@ class Base3DOptions(VariantOptions):
var = match.group(1)
slots = match.group(2).split(',') if match.group(2) else []
# Do the match
if var.startswith('_kicost.'):
# KiCost specific matching system
matched = var_re and var_re.match(var[8:])
elif var == '_default_':
if var == '_default_':
default = slots
if self.extra_debug:
logger.debug('- Found defaults: {}'.format(slots))
@ -242,6 +237,17 @@ class Base3DOptions(VariantOptions):
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_')

View File

@ -43,6 +43,10 @@ class BaseVariant(RegVariant):
""" Returns the name of the field used to determine if the component belongs to the variant """
return None
def matches_variant(self, text):
""" This is a generic match mechanism used by variants that doesn't really have a matching mechanism """
return self.name.lower() == text.lower()
def filter(self, comps):
# Apply all the filters
comps = apply_pre_transform(comps, self.pre_transform)

View File

@ -68,12 +68,11 @@ class KiBoM(BaseVariant): # noqa: F821
# Config field must be lowercase
self.config_field = self.config_field.lower()
def _variant_comp_is_fitted(self, config):
def matches_variant(self, config):
""" Apply the variants to determine if this component will be fitted.
value: component value (lowercase).
config: content of the 'Config' field (lowercase). """
config: content of the 'Config' field. """
# Variants logic
opts = config.split(",")
opts = config.lower().split(",")
# Exclude components that match a -VARIANT
for opt in opts:
opt = opt.strip()
@ -99,8 +98,8 @@ class KiBoM(BaseVariant): # noqa: F821
if not (c.fitted and c.included):
# Don't check if we already discarded it
continue
config = c.get_field_value(self.config_field).lower()
c.fitted = self._variant_comp_is_fitted(config)
config = c.get_field_value(self.config_field)
c.fitted = self.matches_variant(config)
if not c.fitted and GS.debug_level > 2:
logger.debug('ref: {} config: {} variant: {} -> False'.
format(c.ref, config, self.variant))

View File

@ -53,32 +53,38 @@ class KiCost(BaseVariant): # noqa: F821
self.separators = ' '
else:
self.separators = '['+self.separators+']'
if self.variant_field and self.variant:
self._var_re = re.compile(self.variant, flags=re.IGNORECASE)
else:
self._var_re = None
def matches_variant(self, variants):
if self._var_re is None or not variants:
return True
# The component belongs to one or more variant
for v in re.split(self.separators, variants):
if self._var_re.match(v):
# Matched, remains
return True
# None of the variants matched
return False
def filter(self, comps):
GS.variant = [self.variant]
comps = super().filter(comps)
logger.debug("Applying KiCost style variant `{}`".format(self.name))
if not self.variant_field or not self.variant:
if self._var_re is None:
# No variant field or not variant regex
# Just skip the process
return comps
# Apply to all the components
var_re = re.compile(self.variant, flags=re.IGNORECASE)
for c in comps:
logger.debug("{} {} {}".format(c.ref, c.fitted, c.included))
if not (c.fitted and c.included):
# Don't check if we already discarded it
continue
variants = c.get_field_value(self.variant_field)
if variants:
# The component belong to one or more variant
for v in re.split(self.separators, variants):
if var_re.match(v):
# Matched, remains
break
else:
# None of the variants matched
c.fitted = False
if GS.debug_level > 2:
logger.debug('ref: {} value: {} -> False'.format(c.ref, c.value))
c.fitted = self.matches_variant(variants)
if not c.fitted and GS.debug_level > 2:
logger.debug('ref: {} value: {} variants: {} -> False'.format(c.ref, c.value, variants))
return comps