(3D) Build Specifications

Config files

master.json

Vendor-specific. Specifies how the information will be presented in the layout, including the order of the options and exclusion.

{
    // the name of the brand/vendor associated with the build for which this `master.json`
    // file is going to be used, should be coherent with naming in directories
    "brand" : "swear",

    // the sequence of ISO-15897 standard locale definitions for which proper locale
    // files should exist in this build, may not be overridden on a `spec.json` level
    // order is not irrelevant and the first locales in the list are considered to be
    // more "relevant" than the last ones (for instance for a fallback decision)
    "locales" : ["en_us", "pt_br", "ar"],

    // defines the order in which the original file structure should be presented in the layout
    "order" : {
        // the order in which the models appear in the layout (a model which is not
        // here won't appear in the catalog)
        "models" : ["vyner", "maddox", (...)],

        // the order in which the parts appear in the layout (both the alias and the
        // original names are present)
        "parts" : ["vamp", "front", "quarter", "lower_quarter", "ankle_quarter", "back", (...)],

        // the order in which the materials appear in the layout (again, both the alias and
        // the original names are present)
        "materials" : {
            "vamp" : ["nappa", "suede", "metallic", "hairy_calf", "ostrich", "python", "croc"],
            "front" : ["nappa", "suede", "metallic", "hairy_calf", "ostrich", "python", "croc"],
            (...)
        },

        // the order in which the colors appear in the layout (again, both the alias
        // and the original names are present)
        "colors" : {
            "nappa" : ["white", "black", "navy_blue", "royal_blue", "red", "fuchsia_pink", "chocolate_brown"],
            (...)
        }
    },

    // you can prevent combinations of appearing in the layout by explicitly
    // stating by part, materials per part and colors per material, you can
    // also prevent combinations by creating "rules" with "open" dimensions
    // using the empty string value (eg: `["", "vege_tan_calf", ""]`)
    "blacklist" : {
        "parts" : ["side", "top"],
        "materials" : {
            "side" : ["nappa", "python"],
            "upper" : ["calf"]
        },
        "colors" : {
            "cotton" : ["silver"],
            "metal" : ["pewter", "fuchsia_pink", "navy_blue"]
        },
        "rules" : [
            ["sole", "rubber", "gold"],
            ["sole", "rubber", "copper"],
            ["", "vege_tan_calf", ""],
            ["", "metallic", "pewter"],
            ["", "metallic", "fuchsia"],
            ["", "metallic", "navy"]
        ]
    },

    // defines the part's priorities for drawing, this should
    // ensure proper layering of the model (no hidden parts)
    "z_index" : {
        "shadow" : 1,
        "lining" : 2,
        "back_stay" : 3,
        "front" : 4,
        "side" : 5,
        "metal_toe_cap" : 10000,
        "overlay" : 30000
    },

    // allows definitions of specific z index values for certain
    // frames allowing a definition with more precedence than `z_index`
    // should allow both exact frame match and frame ranges
    "z_index_m" : {
        "side-000:side-003" : {
            "shadow" : 1
        },
        "side-006": {
            "front" : 40,
            "side" : 50
        }
    },

    // defines visual alias for part, materials or colors, note that a fully
    // qualified name may be used to reduce ambiguity
    "alias" : {
        "vamp" : "vamper",
        "cotton" : "coton",
        "vamp:cotton" : "contone"
    }
}

config.json

Vendor-specific. Used to set the params of the building scripts (all.py).

{
    // vendor's name (myswear, swear, chanel, saint_laurent, ...).
    "vendor" : "myswear",

    // the z-index of each part, used by color.py to determine the correct picking
    // mask (values default to 100), usually shadow has the lowest value (appears
    // below) and the optionals have the highest (appear above), non-optional parts
    // usually do not need to be defined (when they come from 3D they are already
    // mutually exclusive and cropped by z-index)
    "priorities" : {
        "shadow" : 1,
        "fringe" : 10000,
        "metal_toe_cap" : 10000,
        "metal_caps" : 10000,
        "strap_tip" : 10000,
        "strap_tips" : 10000,
        "fringe_eyelets" : 10001,
        "toe_cap" : 20000,
        "overlay" : 30000
    },

    // avoids the color.py step where the masks are created,
    // the default is true, should be set carefully to avoid
    // corrupted and malfunctioning 3DBs
    "colorize" : false,

    // defines the color to be used when creating the masks,
    // the default is `000000`
    "color_colorize" : "ffffff",

    // avoids the `alpha.py` step where the alpha composition of images is applied,
    // the default is true
    "alpha_composite" : false,

    // to be ignored by `color.py`, since they are not parts folders, alternatively
    // this may be used to avoid the creation of masks for the certain parts as
    // they are not visible in the final layout
    "skip_colorize" : ["fringe_shadow", "masks", "overlay"],

    // the parts that are going to be ignored for the colorize operation, this is
    // different from the skip colorize in the sense that the index value is still
    // going to be counted towards the global mask (required to avoid incorrect
    // calculus of part index at runtime) and the picker (single) mask is still
    // going to be generated for the part
    "ignore_colorize" : ["inner_buckle"]

    // defines a specific material to be used for the colorize operation
    // for certain part of a model, in case the material is not defined using
    // this field a "random" one is used instead (not predictable)
    // an alternative notation (`$path$`) exists to define an external path to
    // a directory of images to be used in the colorize process of the part, this
    // allows even more control to the way masks are generated for a certain part,
    // the name of the images in such path should respect the canonical representation
    // (eg: `front-001.png`)
    "specific_colorize" : {
        "vyner" : {
            "front" : "nappa",
            "side" : "nappa",
            "laces" : "$path$/static/colorize/vyner/laces"
        }
    },

    // y-offset to be applied in a vertical translation
    "repositions" : {
        "regent" : 140,
        "bond" : 164,
        "maltby" : 78,
        "carnaby" : 78
    },

    // to be ignored by reposition.py, usually the top frame and the overlay
    // folder, since those files should already have the proper vertical alignment
    "skip_repositions" : ["overlay", "top"],

    // parts where opacity_shadow.py should apply, these parts are also going to be
    // ignored for the color.py
    "shadows" : ["fringe_shadow", "shadow"],

    // opacity_shadow's parameters (setting both to 1 ignores the operation)
    "opacity_shadow" : {
        "default" : {
            "blur_radius" : 1,
            "alpha_divisor" : 1
        },
        "fringe_shadow" : {
            "blur_radius" : 3.5,
            "alpha_divisor" : 2
        }
    },

    // merges two parts, the first value in the key-value pair appears below
    "blend_shadow" : {
        "maltby" : {
            "fringe_shadow" : "fringe"
        }
    },
    "no_alpha_composite" : ["white"],
    "to_remove" : [".DS_Store", "Thumbs.db", "thumbs.db"],

    // folders to include in the build (by copying)
    "static_dirs" : ["overlay"],

    // if the verification process on the building should be avoided
    // note that the avoiding verification is a dangerous operation
    "skip_verify" : false,

    // avoids a step of verify.py where it checks if the parts/materials/colors
    // present in spec.json exist
    "skip_verify_defaults" : ["masks", "overlay"],

    // creates a "croc_beya_loafer" which is a copy from "beya_loafer"
    // but where parts with "alligator_kirk" only have that material
    "new_models" : {
        "beya_loafer" : {
            "model_name" : "croc_beya_loafer",
            "material_to_extract" : "alligator_kirk"
        }
    }
}

spec.json

Model-specific. Every model has one as listed below, besides also supporting some of the settings in master.json such as order, blacklist, alias and restrictions (if one of these properties is present in both files, they will be merged, and the settings in spec.json takes priority).

{
    // the compatibility level of the current specification, the hight this value
    // is the less retro-compatible the runtime and build time solution will be,
    // by breaking with compatibility features may become available (defaults to `1`)
    // check compatibility levels section for more information
    "compatibility" : 1,

    // if the current model configuration should be considered enabled by the runtime
    // if set to false the model is ignored (defaults to `true`)
    "enabled" : true,

    // sequence that defines the multiple "render" strategies that are available
    // for the model in question (eg: `prc`, `csr`, `ssr`, etc.)
    "strategies": ["prc", "csr"],

    // the (tech) name of the brand associated with this model, should be
    // typically consistent with the vendor name for the build (bundle)
    "brand" : "swear",

    // to be displayed in the layout
    "description" : "vyner",

    // to be displayed in the layout
    "title" : "lace up round toe",

    // coma separated sequence of authors of the model,
    // should comply with the standard email format
    "authors" : "Tobias Matias <tm@platforme.com>, Matias Tobias <mt@platforme.com>",

    // describes the model's style (eg: high, low top, etc.) should be used
    // carefully and the usage of the `tags` field is now encouraged for a more
    // "detailed" description of the model
    "style" : "low",

    // the default scale to be used in the sizes of the model, can be defined
    // as a string or an object (eg: `{"male" : "it", "female" : "fr"}`) if different
    // scales should be applied per gender, in case this value is not provided
    // explicitly it is inferred (if possible) from the values defined in the `sizes` field
    "scale" : "it",

    // {both, male, female, kids}, affects how the catalog's filter works
    "gender" : "both",

    // to be displayed in the layout
    "observations" : "A statement high-top sneaker detailed with four velcro straps.",

    // {round, pointy}, affects how the catalog's filter works, the semantic
    // value of this field is low and it's considered deprecated
    "toe" : "round",

    // related to the image resolution, use "very-large" by default (1000x1000)
    // this value has only a descriptive meaning with no "real value"
    "type" : "very-large",

    // the size of the base composition images in pixels, if not provided this
    // value may be inferred from the dimensions `$base` field
    "size" : [1000, 1000],

    // the format of the base files for PRC (eg: "png", "jpeg", "webp", etc) if not
    // provided it may be inferred from the dimensions `$base` field
    "format" : "png",

    // defines preferred pre-computed composition dimensions, additionally
    // other settings can be also defined such as format, color depth and quality
    // the special `$base` item defines the resolution of the base (original) images
    // these values may be used by tools like RIPE Building to pre-generated the
    // assets for non base dimensions according to parameters
    "dimensions" : {
        "$base" : {
            "size" : [1000, 1000],
            "format" : "png",
            // this optional property only applies to "$base", and it means that
            // the source slices will be transformed to comply with what is defined
            // in this specification (e.g. source images are 2000x2000 PNGs and they
            // will become 1000x1000 PNGs after the transformation operation runs)
            "transformed": true,
            "faces" : {
                "zoom" : {
                    "size" : [2000, 2000],
                    "format" : "png"
                }
            }
        },
        "small" : {
            "size" : [500, 500],
            "format" : "png",
            "faces" : {
                "zoom" : {
                    "size" : [1000, 1000],
                    "format" : "png"
                }
            }
        },
        "jpeg" : {
            "size" : [1000, 1000],
            "format" : "jpeg",
            "background" : "9d9d9d",
            "options" : {
                "quality" : 80,
                "optimize" : true
            },
            "faces" : {
                "zoom" : {
                    "size" : [2000, 2000],
                    "format" : "jpeg",
                    "background" : "9d9d9d",
                    "options" : {
                        "quality" : 80,
                        "optimize" : true
                    }
                }
            }
        }
    },

    // map that defines the multiple 3D meshes that are available for the model
    // (to be used by CSR and SSR) each of them should have a proper semantic
    // value to be used for runtime decisions, these meshes should be placed
    // under the model's `meshes` directory
    // most of the fields are optional and should be inferred, for instance the
    // `format` field should be inferred by `file` extension in case it's not provided
    "meshes" : {
        "$base" : {
            "file": "base.glb",
            "format": "glb",
            "polycount": "high"
        },
        "$base.fbx" : {
            "file": "base.fbx",
            "format": "fbx",
            "polycount": "high"
        },
        "low" : {
            "file": "low.glb",
            "polycount": "low"
        },
        "low.fbx" : {
            "file": "low.fbx",
            "polycount": "low"
        }
    },

    // keywords that define the behavior or characteristics of the model
    // "generic" is used when the model is considered compliant with generic standard model definition (eg: engraving complies with properties definition)
    // "private" is used then the model data should be available to authorized clients
    // "no_masks" is used when the model should not use masks
    // "no_size" is used when the model has no size to choose
    // "no_initials" is used when the model has no personalization
    // "no_customization" is used when the model has no customization
    // "initials_lowercase" initials are normalized in an lowercase form
    // "initials_uppercase" initials are normalized in an uppercase form
    // "initials_dot" initials are normalized with a dot separator in between letters
    // "initials_mandatory" is used when the personalization is required to advance to the next step
    // "initials_type_full" is used to show the 'full' model's image in the personalization step, when choosing the initials
    // "initials_type_zoom" is used to show a 'zoomed' image of the model (circular form) in the personalization step, when choosing the initials
    // "initials_no_radius" is used to avoid border-radius in a model's image in the personalization step, when choosing the initials
    // "farfetch_single_step" is used when the full customization is done in a single step on the farfetch plugin
    // "farfetch_no_masks" is used when the model has no masks on the farfetch plugin
    // "farfetch_size_step_no_initials" is used to hide the image with the initials on the size step of the farfetch plugin
    "tags" : ["initials_mandatory"],

    // describes the `factory` that is manufacturing the model
    // and some producing related information such as, if multiple
    // factories exist that are able to produce this model the `factories`
    // key should be used instead with a sequence of factories, note that
    // the `factories` can always be generated if only the `factory` is set
    // "production_time" production time in days
    // "production_cost" production cost in system's default currency
    // "locale" the locale in which the factory operates
    "factory" : {
        "name" : "FLAJ",
        "production_time" : 5,
        "production_cost" : 50,
        "locale" : "it_it_tech"
    },
    "factories" : [{
        "name" : "FLAJ",
        "production_time" : 5,
        "production_cost" : 50,
        "locale" : "it_it_tech"
    }, {
        "name" : "ASIAL",
        "production_time" : 6,
        "production_cost" : 70,
        "locale" : "pt_pt_tech"
    }],

    // describes the size range (in native) for the current model using either
    // a range (see https://docs.python.org/3/library/stdtypes.html#ranges)
    // based object that contains the lower bound (start) and the upper bound
    // (end) not inclusive or an array of explicit values, notice that the key
    // defines both the scale and the gender separated by the `:` character
    "sizes" : {
        "it:male" : {
            "start" : 25,
            "end" : 47,
            "step" : 2
        },
        "it:female" : [
            19,
            21,
            23,
            26,
            29,
            31
        ]
    },

    // list that represents the other size scales in which this model can be
    // represented in, to be used by associated UI consumers in size selection,
    // this value is considered to be "pure" meta-information
    "available_scales": ["eu", "us", "uk"],

    // legacy field that describes the number of frames images available for the
    // the main/first face, typically the side one (legacy)
    "frames" : 24,

    // possible perspectives of the model, notice that the first face is considered
    // the main one and subject to the defined default number of frames, this also
    // defines the default ordering of the faces (legacy)
    "faces" : ["side", "top"],

    // map version of the faces definition that allows a more flexible and powerful
    // description the faces, if this map does not exist it is going to be constructed
    // from the `faces` and `frames` fields, a face is described by the number of `frames`
    // (mandatory field) and if the face is recommended to be used as a `thumbnail` (optional field)
    "faces_m" : {
        "side" : {
            "frames" : 24,
            "thumbnail" : true
        },
        "top" : {
            "frames" : 1,
            "thumbnail" : false
        }
    },

    // possible videos that showcase the model, contains the names of the videos and
    // also defines the default ordering of the videos
    "videos": ["rotation", "drop"],

    // map version of the videos definition that allows more flexible and powerfull
    // description of the videos, it contains the name of each video of the model
    // and data about it, more specifically a `description` of the video, if it is
    // recommended to be used as a `thumbnail` (optional field), the `format` of
    // the video file (optional field), which defaults to `mp4`, and if the video
    // should `loop` when playing (optional field), which defaults to `true`
    "videos_m": {
        "rotation": {
            "description": "A video showing the rotation of the cube",
            "thumbnail": true,
        },
        "drop": {
            "description": "A video showing the cube being droped",
            "thumbnail": true,
            "format": "mov",
            "loop": false
        }
    }

    // the sequence of faces (and frames) and videos that are going to be used as the
    // meta-information for the generation of (possible) thumbnails in the UI, should be
    // considered just meta information responsible for conditioning the UI and not
    // something else, a typical usage of these values is to merge them against static
    // definition on the runtime UI there's a compressed version of the description that
    // allows someone to just define a string with the face and possibly the frame to
    // define the thumbnail, notice that extra metadata like `overlay` may be added with
    // more information for a certain thumbnail in this case requesting that an overlay
    // should exist for that face and frame, these settings should match the definition
    // of the faces (either `faces` or `faces_m`), if a thumbnail is supposed to be used
    // only for personalization, it should have `type` as `personalization` and a `group`
    // can be specified, or else it will to `main`, it can override the frame used if
    // a `frame` and `face` are provided, it can provide `profiles` that take precedence
    // on the thumbnail image, and videos can also be defined by providing the video name
    // and the type of file `video`
    "thumbnails" : ["face.<frame>", "top", "side.0", "side.6"],
    "thumbnails" : [
        {
            "name" : "top",
            "face" : "top",
            "overlay" : true
        },
        {
            "name" : "side",
            "face" : "side",
            "frame" : 0
        },
        {
            "name" : "back",
            "face" : "side",
            "frame" : 6
        },
        {
            "name" : "personalization",
            "type": "personalization"
            "group": "side"
        },
        {
            "name" : "personalization-frame",
            "type": "personalization"
            "face": "side",
            "frame": 6
        },
        {
            "name" : "personalization-profile",
            "type": "personalization"
            "profiles": ["style::red"]
        },
        {
            "name": "rotation",
            "type": "video"
        }
    ],

    // parts not to be displayed in the layout, should also be added to defaults (see below) as hidden
    "hidden" : ["shadow"],

    // same as the order definition in master.json (overrides)
    "order" : {},

    // same as the blacklist definition in master.json (overrides)
    "blacklist" : {},

    // same as the alias definition in master.json (overrides)
    "alias" : {},

    // specifies the frame to be used to place the initials, based on
    // the engraving material, default is applied if no material rule
    // is found (fallback)
    "initials_frame" : {
        "default" : "top",
        "metal" : 18
    },

    // list of materials for which the dot character is going to be
    // place in between the two letters for the initials
    "initials_dot_materials" : [
        "embossed"
    ],

    // defines the set of rules (material a then color) that when matched
    // on the pivot part (usually front) will trigger the usage of the patch
    // and using the material defined in the leaf node
    "initials_patch" : {
        "suede" : {
            "white" : {
                "material" : "nappa",
                "color" : "white"
            }
        }
    },

    // the pivot part: the one whose material is used to determine
    // if the initials will be placed on a patch. It normally is the
    // part where the initials are applied to
    "initials_pivot_part" : "front",

    // the part whose material will be used to determine if a patch
    // should be used or not
    "initials_patch_materials" : "front",

    // list of initials materials for which the placing of the initials is
    // going to be done on a patch instead of the "normal" direct printing
    // of the initials on the initials frame pivot part
    "initials_patch_materials" : [
        "metal"
    ],

    // when set to true, the model will use a patch for the initials printing
    // regardless of the pivot part's material, this allows simplification of
    // model's configuration file so that there's no need to define the complete
    // set of materials of the model in the `initials_patch` map, the material
    // and color to be used in the patch is the one defined for the pivot part
    // in case the material color for patch does not exist the patch apply operation
    // is ignored and normal "apply" of initials is used instead
    "always_patch" : true,

    // describes sets of optional parts where mutual exclusion is going to take place, meaning that
    // if one is visible the other ones are going to become invisible
    "exclusions" : {
        "toe_cap_exclusion" : ["toe_cap", "metal_toe_cap"]
    },

    // describes sets of optionals parts where when one is visible, the others in the set also are
    "groups" : {
        "fringe_group" : ["fringe", "fringe_eyelets"]
    },

    // default material and color for every part and the definition of optional,
    // there's also some other information defined in this object that includes
    // (default) `face`, (default) `frame` and `thumbnail`
    // all these data definitions are derived from the `parts` attribute declared above
    "defaults" : {
        // thumbnail is set to `true` meaning that this value is going to
        // "automagically" included in the `thumbnails` list, it also defines
        // frame 2 of the front face as the one that best displays the front part
        "front" : {
            "material" : "nappa",
            "color" : "white",
            "face" : "front",
            "frame" : 2,
            "thumbnail" : true
        },

        // metal toe cap is considered to be optional, meaning that it
        // must be explicitly requested to be part of the final composition
        "metal_toe_cap" : {
            "optional" : true
        },

        // fringe is optional but it defaults to a visible state, meaning
        // that it can be set to invalid but by default it's going to be
        // displayed as part of a configuration
        "fringe" : {
            "optional" : true,
            "material" : "nappa",
            "color" : "white"
        },

        "shadow" : {
            "hidden" : true,
            "material" : "default",
            "color" : "default"
        }
    },

    // unstructured meta-data information, should be divided into different
    // contexts for good information isolation, either by placing them within
    // a context identifier map or by prefixing the keys with the context identifier
    "meta" : {
        "farfetch" : {
            "show_phone" : false
        },
        "ff_bounding_specs" : {
            "000" : {
                "suffix" : "_v1"
            },
            "002" : {
                "width" : 890,
                "suffix" : "_v2"
            },
            "top" : {
                "height" : 1200,
                "rotation" : 270,
                "suffix" : "_v4"
            }
        },
        "retail" : {
            "show_notification" : false
        }
    },

    // describes technical data used during the visual assets production
    // the data contained here should be separated by context and should
    // be interpreted according to such context
    "technical" : {
        "maya" : {
            "displacements" : {
                "parts" : ["front", "fringes", "side"],
                "ao_pass" : {
                    "main" : {
                        "type" : 1,
                        "amount" : 0.03,
                        "shift" : 0.0,
                        "edge_length" : 0.1,
                        "max_subdivs" : 8
                    },
                    "front" : {
                        "amount" : 0.06,
                        "shift" : 0.0,
                        "edge_length" : 0.1,
                        "max_subdivs" : 8
                    }
                },
                "materials" : {
                    "nappa" : {
                        "amount" : 0.03,
                        "shift" : 0.0,
                        "edge_length" : 0.15,
                        "max_subdivs" : 8
                    }
                }
            },
            "matchings" : {
                "front" : {
                    "materials" : {
                        "nappa" : {
                            "colors" : {
                                "black" : ["red", "green"],
                                "white" : ["grey", "blue"]
                            }
                        }
                    }
                }
            },
            "render_layers" : {
                "front" : {
                    "adjustments" : ["vraySettings.dmcMaxSubdivs", "vraySettings.dmcThreshold"],
                    "values" : [32, 0.001]
                },
                "fringe" : {
                    "adjustments" : ["MainDomeShape.diffuseContrib"],
                    "values" : [0.95]
                }
            }
        }
    }
}

initials (spec.json)

{
    // the name of the profile that is going to be applied by default
    // in every initials composition
    "profile" : "main",

    // the name of the frame (may include face) that is going to be used
    // for the display of the "special" initials compose request
    "frame" : "top",

    // the algorithm to be used in the blending operation between the initials
    // image and the target image (eg: `mask_top`, `destination_over`, `source_over`, etc.)
    "algorithm" : "mask_top",

    // defines the behaviour that is going to be used in case of bounding
    // box overflow of the initials (eg: `hidden`, `visible`)
    "overflow" : "hidden",

    // name of the font that is going to be used for the "drawing" of the
    // initials, the specific name of the font is constructed by appending
    // the `font-family` with the `font-weight` (eg: DoraBlack-Regular.fnt)
    "font_family" : "DoraBlack",
    "font_weight" : "Regular",

    // the kind of mask that is going to be used upon the rendering of the
    // font to the target initials image (eg: `simple`, `self`)
    "font_mask" : "simple",

    // list of parts that are meant to be excluded from the composition
    // when a valid initials value exists
    "exclusion" : [
        "logo"
    ],

    // defines the control point coordinates for a bezier curve that is going
    // to be used in the placement of the initials
    "curve" : [
        [0.2, 0.2],
        [0.7, 0.2],
        [0.2, 0.5],
        [0.7, 0.5]
    ],

    // defines the control point coordinates for the bezier curves that are going
    // to be used in the placement of the initials, if defined overrides the
    // value provided in the `curve` value
    "curves" : [
        [
            [0.2, 0.2],
            [0.7, 0.2],
            [0.2, 0.5],
            [0.7, 0.5]
        ],
        [
            [0.1, 0.2],
            [0.4, 0.2],
            [0.2, 0.8],
            [0.2, 0.5]
        ]
    ],

    // list of parts or triplets for parts that are going to be included
    // in case a valid initials value exists, if a single value is provided
    // the material and the color is assumed to be `default`
    "inclusion" : [
        "patch",
        "initials_patch:nappa:green"
    ],

    // the z-index to be used in the composition when "drawing"/"painting" the initials
    // into the final composition value, this is used as part of the dynamic layer strategy
    // used for the display of the initials on the image composition
    "z_index" : 4,

    // defines the alignment of the text within the defined bounding box,
    // possible values include: `left`, `right`, `center`, `justified`
    "align" : "left",

    // defines the mode of line break used for the text within the defined bounding box,
    // possible values include: `null`, `normal`, `word_break`
    "line_breaking": "normal",

    // defines the letters to be used by the line breaking "word_break" logic to separate
    // words and apply the word breaking logic, if null it invalidates the
    "word_spacing_letter": " ",
    "word_dividing_letter": "-",

    // defines that a word (using line breaking "word_break") that does not fit
    // inside the space for initials (bounding box or bezier curves) should still
    // be drawn in the last space available and, in combination with overflow
    // 'hidden', will appear cut
    "line_break_overflow": false,

    // the definition of the multiple properties (characteristics) of the initials
    // that can be used at runtime for the construction of the `engraving` value,
    // there are two ways of defining `properties`: the compressed form with strings
    // that defined both the name of the property and optionally the type of it and
    // a the canonical way that defines each property as an object of values that
    // should at least define the name of the property
    // by using these values one may create a valid `engraving` value of `silver.opensans:font`
    // or in alternative a more complete `gold:style.opensans:font:` giving proper
    // context for the interpretation of the `engraving` value
    "properties" : ["name.<type>", "silver.style", "gold.style", "opensans.font"],
    "properties" : [{ name: "silver", type: "style" }],

    // description of the context-aware profiles that can be used to control
    // the initials parameters activation for certain situations, most of the
    // parameters associated with initials can be used under profiles
    "$profiles" : {
        "base" : {
            "frame" : "top",
            "font_family" : "DoraBlack",
            "font_weight" : "Regular"
        }
    },

    // the set of "pseudo-profiles" that are going to target the concrete ones
    // and that allow an extra level of indirection that can be used to better
    // structure the information, wildcard expansion is supported
    "$alias" : {
        "report" : "viewport::large",
        "step::personalization" : ["viewport::large*"]
    },

    // defines the parameters related specifically to the CSR environment
    "csr": {
        // the width in pixels of the initials canvas
        // this allows the control of the bounding box size
        // the default value is `3000`
        "width": 3000,

        // the height in pixels of the initials canvas
        // this allows the control of the bounding box size
        // the default value is `300`
        "height": 300,

        // defines the control points coordinates in the 3D space for the curvature of the
        // initials plane
        // the list of coordinates is defined by an array containing the multiple control points
        // with each control point being itself defined by an array [x, y, z] which dictates the
        // point position in the 3D space
        // the default value is `[]`
        "points": [
            [-1, 0, 0],
            [0, 0, -1],
            [1, 0, 0]
        ],

        // the type of Catmull–Rom splines to use for the curvature of the initials plane
        // "centripetal" is used to the apply Centripetal variant
        // "chordal" is used to apply the Chordal variant
        // "catmullrom" is used to apply the Catmullrom variant
        // the default is `"centripetal"`
        "curve_type": "centripetal",

        // the tension of the curve (it only affects curves of type "catmullrom")
        // the default value is `0.5`
        "curve_tension": 0.5,

        // defines the position coordinates of the initials plane in the 3D space
        // the coordinates are defined by an array [x, y, z] detailing the values for each axis
        // being that a positive value for the y axis represents up
        // the default value is `[0, 0, 0]`
        "position": [0, 0, 0],

        // defines the rotation of the initials plane in the 3D space
        // the coordinates are defined by an array [x, y, z] detailing the values in degrees for
        // each axis
        // the default value is `[0, 0, 0]`
        "rotation": [0, 0, 0],

        // defines the scale of the initials plane in the 3D space
        // the value is used to do a scalar multiplication for the initials plane, meaning that
        // each axis will be multiplied by the scale value thus affecting the actual size of the
        // initials plane in the 3D space
        // it is preferred to use the "width", "height" and "font_size" fields to obtain the
        // desired size and quality for the initials text and only use "scale" in situations
        // where, for example, the initials are very small and it's not possible to get good high
        // resolution and high quality text because to achieve the desired size, the "width",
        // "height" and "font_size" values would also have to be very small thus the text
        // resolution would very poor and could introduce artifacts on the extruded 3D text
        // in those cases the solution would be to just maintain an high resolution and scale down
        // the initials plane in the 3D space
        // the default value is 1
        "scale": 1,

        // defines the parameters for the rendered text
        "text": {
            // the font size value in pixels used by the initials canvas
            // the default value is `280`
            "font_size": 280,

            // name of the font that is going to be used for the "drawing" of the initials, the
            // specific name of the font is constructed by appending the `font-family` with the
            // `font-weight` (eg: DoraBlack-Regular.ttf)
            // the default font used is Arial if no font_family and no font_weight is specified
            "font_family" : "DoraBlack",
            "font_weight" : "Regular",

            // the format of the font being used by the initials canvas
            // the default value is `ttf`
            "font_format" : "ttf",

            // the number of pixels to offset in the x axis from the center
            // the default value is `0`
            "x_offset": 0,

            // the number of pixels to offset in the y axis from the center
            // the default value is `0`
            "y_offset": 0,

            // the thickness of the lines used when drawing the text in the initials canvas
            // this allows the control of the surface area of the text texture meaning that it controls
            // how much of the text texture is applied to the sides of the extruded 3D text
            // a value of `1` will make the text texture have the same thickness of the displacement
            // texture text, which will make the sides of the extruded 3D text transparent thus providing
            // the effect of a flat 2D text in a 3D environment instead of a 3D text
            // setting the value greater than `1` will make the text texture thicker than the displacement
            // texture text which means that the text texture will start to fill the sides of the 3D text
            // the default value is `5`
            "stroke_width": 5,

            // the value of the standard deviation to the Gaussian function used by the initials
            // canvas when generating the displacement texture, thus a larger value will create more
            // blur, which means that the larger the value the more rounded and smoothed the extruded
            // 3D text will be
            // the default value is `1.5`
            "displacement_blur": 1.5,

            // the intensity of the Gaussian blur applied by the shader that handles the generation
            // of the initials text normal texture, which means that a larger value will create more
            // blur, which means that the larger the value, the more rounded and smoothed the texture
            // will be, thus resulting in the extruded 3D text having less harsh light bounces and
            // giving the illusion that the geometry is more rounded
            // the default value is `1`
            "normal_map_blur": 1,
        },

        // defines the parameters for the initials material
        "material": {
            // defines the base color of the initials material,
            // the default is `ffffff`
            "color": "ffffff",

            // defines the metalness of the initials material,
            // the value ranges from 0 to 1
            // the default value is `0`
            "metalness": 0,

            // defines the roughness of the initials material,
            // the value ranges from 0 to 1
            // the default value is `1`
            "roughness": 1,

            // defines the emissive (light) color of the material, essentially a solid color unaffected
            // by other lighting
            // the default value is `000000`
            "emissive_color": "000000",

            // the intensity of the the emissive light of the material, it modulates the emissive color
            // the default value is `1`
            "emissive_intensity": 1,

            // the scale of how much the displacement map affects the mesh, meaning that it controls the
            // initials 3D text extrusion
            // the greater the value the more extruded the 3D text will be and the smaller the value the
            // more flattened the 3D text will be until
            // it supports negative values meaning that we can extrude in the reverse direction thus
            // obtaining inwards 3D deformation
            // the default value is `25`
            "displacement_scale": 25,

            // the offset of the displacement map's values on the mesh's vertices
            // the default value is `0`
            "displacement_bias": 0
        },

        // defines the parameters for the initials mesh
        "mesh": {
            // the number of segments in the x axis of the initials plane mesh
            // the greater the number, the larger the number of horizontal segments in the initials plane
            // which means a greater number of vertex per pixel and greater 3D text quality
            // the default value is `1000`
            "width_segments": 1000,

            // the number of segments in the y axis of the initials plane mesh
            // the greater the number, the larger the number of vertical segments in the initials plane
            // which means a greater number of vertex per pixel and greater 3D text quality
            // the default value is `100`
            "height_segments": 100
        },

        // defines the parameters for the base texture that is used for the diffuse map of the initials
        "base_texture": {
            // the name of the texture to be used for the diffuse map
            "name": "pattern_xyz",

            // the name of the wrapping method that dictates how the texture is wrapped horizontally
            // corresponds to U in UV mapping
            // "repeat" is used to have the texture repeat to infinity
            // "clamp_to_edge" is used to have the edge clamped to the outer edge texels
            // "mirrored_repeat"  is used to have the texture repeat to infinity, mirroring on each repeat
            // the default is `"repeat"`
            "wrap_s": "repeat",

            // the name of the wrapping method that dictates how the texture is wrapped horizontally
            // corresponds to V in UV mapping
            // "repeat" is used to have the texture repeat to infinity
            // "clamp_to_edge" is used to have the edge clamped to the outer edge texels
            // "mirrored_repeat"  is used to have the texture repeat to infinity, mirroring on each repeat
            // the default is `"repeat"`
            "wrap_t": "repeat",

            // defines how much a single repetition of the texture is offset for each direction
            // the values are defined by an array [U, V] detailing the values typically ranging from 0.0 to 1.0
            // the default value is `[0, 0]`
            "offset": [0, 0],

            // defines how many times the texture is repeated across the surface for each direction
            // the values are defined by an array [U, V]
            // the default value is `[1, 1]`
            "repeat": [1, 1],

            // defines the rotation in degrees of the texture the center point
            // the default value is `0`
            "rotation": 0,

            // defines the center point on which rotation occurs around
            // the values are defined by and array [U, V] where [0.5, 0.5] corresponds to the center of the texture
            // the default value is `[0, 0]` and it corresponds to the the lower left
            "center": [0, 0]
        },

        // defines the parameters for the displacement texture that is used for the displacement map of the initials
        "displacement_texture": {
            // follows the same parameters as "base_texture"
        },

        // defines the parameters for the metallic texture that is used for the metallic map of the initials
        "metallic_texture": {
            // follows the same parameters as "base_texture"
        },

        // defines the parameters for the normal texture that is used for the normal map of the initials
        "normal_texture": {
            // follows the same parameters as "base_texture"
        },

        // defines the parameters for the roughness texture that is used for the roughness map of the initials
        "roughness_texture": {
            // follows the same parameters as "base_texture"
        }
    }
}

3d (spec.json)

{
    // defines the parameters for the scene configuration
    "scene": {
        // the name of the environment to use in the scene
        // this maps to a specific hdr file
        "environment": "studio_small_2",

        // the name of the tone mapping algorithm to use in the scene
        // "none" is used to not apply tone mapping
        // "aces_filmic" is used to apply ACES Filmic tone mapping
        // "linear" is used to apply Linear tone mapping
        // "reinhard" is used to apply Reinhard tone mapping
        // "cineon" is used to apply Cineon tone mapping
        // the default is `"aces_filmic"`
        "tone_mapping": "aces_filmic",

        // the exposure level of tone mapping
        // the default value is `1`
        "tone_mapping_exposure": 0.8,

        // defines the set of properties that control the scene camera
        "camera": {
            // defines the position coordinates of the scene camera in the 3D space
            // the coordinates are defined by an array [x, y, z] detailing the values for each axis
            // being that a positive value for the y axis represents up
            // the default value is `[0, 0, 207]`
            "position": [0, 0, 207],

            // defines the rotation of the scene camera in the 3D space
            // the coordinates are defined by an array [x, y, z] detailing the values in degrees for
            // each axis
            // the default value is `[0, 0, 0]`
            "rotation": [0, 0, 0],

            // the frustum vertical field of view, from bottom to top of view, in degrees
            // the default value is `24.678`
            "fov": 24.678,

            // the film size in millimeters used for the larger axis
            // the default value is `35`
            "film_gauge": 35,

            // the camera frustum aspect ratio, usually the canvas width divided by canvas height
            // the default value is `1`
            "aspect": 1,

            // the camera frustum near plane value
            // the default value is `0.1`
            "near": 0.1,

            // the camera frustum far plane
            // the default value is `10000`
            "far": 10000
        },

        // defines the world space position coordinates used to apply the camera look at, meaning that
        // setting this property will make the camera look at the specified coordinates thus overriding
        // the camera rotation values
        // the coordinates are defined by an array [x, y, z] detailing the values for each axis being
        // that a positive value for the y axis represents up
        // the default value is `null`
        "camera_look_at": [0, 0, 0],

        // defines the set of properties that control the scene zoom functionality
        "zoom": {
            // dictates if the zoom functionality is enabled or not
            // the default value is `true`
            "enabled": true,

            // the limiting minimum zoom factor value allowed
            // the default value is `0.75`
            "min": 0.75,

            // the maximum minimum zoom factor value allowed
            // the default value is `1.5`
            "max": 1.5,

            // the sensitivity level for the zoom functionality
            // the default value is `1`
            "sensitivity": 1
        }
    }
}

Logic Files

Context (ctx)

The context object contains the context of customization for the current environment and it's considered the primary way of sharing information in/out from the hosted application and the build related logic (eg: logic.js).

{
    "brand" : "swear",
    "model" : "vyner",
    "country" : "us",
    "locale" : "en_us",
    "initials" : {
        "left" : {
            "initials" : "J",
            "engraving" : "metal"
        },
        "right" : {
            "initials" : "M",
            "engraving" : "silver"
        }
    },
    parts: {
        "side" : {
            "material" : "metal",
            "color" : "gold"
        }
    },
    choices: {
        "side" : {
            "available": true
            "materials" : {
                "available": true
                "metal" : {
                    "colors" : {
                        "gold" : {
                            "available": true
                        }
                    }
                }
            }
        }
    }
}

logic.py

config.py

class Config(object):

    def on_install(self, ctx):
        print("Thank you for installing the dummy package")
        print("For more information send an email to growth@platforme.com")

    def on_uninstall(self):
        print("We're sorry to see you leave dummy")

base.py

Vendor-specific. General vendor-wise behavior implementation.

import ripe_compose.logic

class Logic(ripe_compose.logic.Logic):

    def groups(self, ctx):
        return ["main"]

    def minimum_initials(self, group, ctx):
        return 1

    def maximum_initials(self, group, ctx):
        return 3

    def supported_characters(self, group, index, ctx):
        return "abcdefghijklmnopqrstuvwxyz"

    def supported_patterns(self, group, index, ctx):
        return ["[A-C]", "文"]

    def spec_to_sku(self, spec):
        body = spec["parts"]["body"]
        top = spec["parts"]["top"]

        product = self.http.get(
            PRODUCT_API,
            params = {
                "model": "fancy"
                "body": "%s.%s" % (body["material"], body["color"])
                "accessory": "%s.%s" % (top["material"], top["color"])
            }
        )
        return product.sku

    def sku_to_spec(self, sku):
        product = self.http.get(
            SKU_API,
            params = {
                "model": "fancy"
                "sku": sku
            }
        )
        return {
            "brand": self.brand,
            "model": self.model,
            "parts": {
                "side": {
                    "material": product["body"].split(".")[0],
                    "color":  product["body"].split(".")[1]
                },
                "top": {
                    "material": product["accessory"].split(".")[0],
                    "color":  product["accessory"].split(".")[1]
                }
            }
        }

    def on_config(self, brand, model, ctx):
        print("Starting config for brand '%s' and model '%s'" % (brand, model))
        ctx["initials"]["main"]["initials"] = "ST"
        return ctx

    def on_part(self, name, value, ctx):
        print(
            "Trying to change part '%s' for material '%s' and color '%s'" %\
            (name, value["material"], value["color"])
        )
        if name == "shadow": value = dict(material = "default", color = "default")
        else: value = dict(material = "leather_dmy", color = "black")
        ctx["parts"][name] = value
        ctx["initials"]["main"]["initials"] = "DM"
        ctx["messages"] = ctx.get("messages", []) + [
            ("config", "Colors forced to black"),
            ("config", "Shadow forced to default")
        ]
        return ctx

    def on_initials(self, group, initials, engraving, ctx):
        print(
            "Trying to change initials to '%s' in '%s' for group '%s'" %\
            (initials, engraving, group)
        )

        blacklist = ["dog", "cat"]
        if initials in blacklist:
            ctx["initials"]["main"]["initials"] = ""
            ctx["messages"] += ctx.get("messages", []) + [
                ("initials", "Blacklisted word detected")
            ]

        return ctx

    def on_model_config(self, model_config):
        print(
            "Trying to change model config for brand '%s' and model '%s'" %\
            (model_config["brand"], model_config["model"])
        )
        model_config["marker"] = True
        return model_config

    def calculate_price(self, model, parts, brand = None, **kwargs):
        price, vat, vat_rate, ddp, ddp_percent, currency = 20.0, 10.0, 0.5, 0.0, 0.0, "EUR"
        return dict(
            total = dict(
                price_final = price + vat + ddp,
                price_taxes = price + vat,
                vat = vat,
                ddp = ddp,
                ddp_percent = ddp_percent,
                vat_rate = vat_rate,
                price = price,
                currency = currency
            )
        )

    def calculate_stock(self, model, parts, brand = None **kwargs):
        return dict(
            total = dict(
                quantity = 1,
                delivery = 24
            )
        )

    def validate_gen(self, ctx):
        for item in ripe_compose.logic.Logic.validate_gen(self, ctx): yield item
        initials = ctx["initials"]["main"]["initials"]
        if initials in blacklist:
            yield "The word '%s' is blacklisted" % initials

model.py

Model-specific. Every model can have its own logic (ie: vyner.py). One can import the base implementation and override the desired behavior.

import base

class Logic(base.Logic):

    def maximum_initials(self, group, ctx):
        return 5

logic.js

{
    /**
     * Returns a boolean that indicates if personalization is allowed
     * for the current context/state.
     *
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Boolean} If personalization is allowed for the current
     * context.
     */
    "allowPersonalization" : function(ctx) {
        return true;
    },

    /**
     * Returns a sequence of names for the groups allowed under the execution
     * of the current context.
     *
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Array} The sequence of allowed groups.
     */
    "groups" : function(ctx) {
        return ["left", "right"];
    },

    /**
     * The minimum number of initials required for the "personalization" step
     * to be considered as valid.
     *
     * @param {Object} group The initials group for which the target number of
     * initials validation is meant to be performed.
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Integer} The minimum amount of initials for the provided group.
     */
    "minimumInitials" : function(group, ctx) {
        return 0;
    },

    /**
     * The maximum number of initials required for the "personalization" step
     * to be considered as valid.
     *
     * @param {Object} group The initials group for which the target number of
     * initials validation is meant to be performed.
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Integer} The maximum amount of initials for the provided group.
     */
    "maximumInitials" : function(group, ctx) {
        return 2;
    },

    /**
     * Returns the valid options for a given group and index, supports use
     * cases where special (Unicode) characters are only allowed on a
     * specific index, like MMI.
     *
     * @param {String} group The name of the group to obtain the sequence
     * of supported characters.
     * @param {Integer} index The initial index withing the group to obtain
     * the sequence of supported characters.
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Array} The sequence of supported characters for the requested group
     * and initials index.
     */
    "supportedCharacters" : function(group, index, ctx) {
        return ["a", "b", " ", "1", "2", "3", "😀", "😎"];
    },

    /**
     * Returns the valid options for a given group and index, supports use
     * cases where special (Unicode) characters are only allowed on a
     * specific index, like MMI.
     *
     * The provided regex patterns should comply with the PCRE standard, so
     * that they can be safely "compiled" by common regex engines.
     *
     * @param {String} group The name of the group to obtain the sequence
     * of supported characters.
     * @param {Integer} index The initial index withing the group to obtain
     * the sequence of supported characters.
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Array} The sequence of supported regex patterns for the
     * requested group and initials index.
     */
    "supportedPatterns" : function(group, index, ctx) {
        return ["[A-C]", "文", "😀"];
    },

    /**
     * Calculates the SKU to a determined configuration spec allowing the
     * usage of "computed SKU's".
     *
     * This method may use external sources for the computation of the SKU
     * (eg: using an HTTP client).
     *
     * @param {Object} spec The object containing the configuration spec.
     * @return {String} The calculated SKU for the current context.
     */
    "specToSku" : function(spec) {
        const codes = {
            "materials": {
                "leather": "LTH",
                "suede": "SDE"
            }
            "colors": {
                "black": "001",
                "blue": "002"
            }
        };

        const side = spec.parts["side"];
        const top = spec.parts["top"];

        const sideCode = `${codes.materials[side.material]}${codes.colors[side.color]}`;
        const topCode = `${codes.materials[side.material]}${codes.colors[side.color]}`;

        return `${sideCode}-${topCode}`;
    },

    /**
     * "Resolves" the configuration spec that matches associated to the
     * provided SKU string, using either internal or external "heuristics".
     *
     * @param {String} sku The SKU to be used in the resolution process.
     * @return {Object} The configuration spec "resolved" from the provided SKU.
     */
    "skuToSpec" : function(sku) {
        const codes = {
            "materials": {
                "LTH": "leather",
                "SDE": "suede"
            }
            "colors": {
                "001": "black",
                "002": "blue"
            }
        };

        const parts = sku.split("-");
        const sideMaterial = parts[0].substring(0, 3);
        const sideColor = parts[0].substring(3, 6);
        const topMaterial = parts[1].substring(0, 3);
        const topColor = parts[1].substring(3, 6);

        return {
            parts: {
                side: {
                    material: `${codes.materials[sideMaterial]}`,
                    color: `${codes.colors[sideColor]}`
                },
                top: {
                    material: `${codes.materials[topMaterial]}`,
                    color: `${codes.colors[topColor]}`
                }
            }
        };
    }

    /**
     * Returns the set of engraving options for the provided context, the
     * reference implementation should be used most of the times using the
     * `properties` field provided by the model's spec file.
     *
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Array} The sequence of engraving options as a name and value
     * paradigm for the current context.
     */
    "engravingOptions" : function(ctx) {
        return [{
            name: "font"
            values: ["medium", "Bold"]
        }, {
            name: "color"
            values: ["white", "black"]
        }]
    },

    /**
     * Validates the current context returning if it's valid according
     * to the basic set of rules (eg: restrictions, sync, etc.).
     *
     * This method should be run both on the client and server side to
     * ensure that for example the order complies with a series of rules
     * for the associated customization.
     *
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Boolean} If the customization associated with the provided
     * context is valid according to domain rules.
     */
    "isValid" : function(ctx) {},

    /**
     * Legacy version of the initials retrieval, should be able to return
     * a string with the main initials value for the model.
     *
     * This method should be used with proper care as the `getInitialsExtra`
     * method should be considered the current canonical version of the initials
     * information retrieval.
     *
     * @return {String} A string with the textual representation of the initials
     * for the current context of configuration.
     */
    "getInitials" : function(ctx) {},

    /**
     * Legacy version of the engraving value retrieval.
     *
     * As with `getInitials` proper care should be taken while using this method
     * as the alternative `getInitialsExtra` method should be used for most cases.
     *
     * @return {String} A string with the textual representation of the engraving
     * type for the current context.
     */
    "getEngraving" : function(ctx) {},

    /**
     * Retrieves the complete initials extra structure ready to be sent
     * to the server side (for order creation).
     *
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Object} The normalized initials extra object ready to be sent
     * to the server side.
     */
    "getInitialsExtra" : function(ctx) {
        return {
            main: {
                initials: ctx.initials.main.value.toUpperCase(),
                engraving: "ada"
            }
        }
    },

    /**
     * Allows the change of the current context of execution, this logic method is
     * to be called on a situation where a customization for a model has been started.
     *
     * @param {String} brand The brand to which the customization session is being started.
     * @param {String} model The model to which the customization session is being started.
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Object} The new update ctx with the desired changes made to the current ctx.
     */
    "onConfig" : function(brand, model, ctx) {
        console.log(`Starting config for brand '${brand}' and model '${model}'`);
        ctx.initials.main.initials = "ST";
    },

    /**
     * Allows the change of the current context of execution, this logic method is
     * to be called on situation where a customization change was made on a part.
     *
     * @param {String} name The name of the part that is going to be changed.
     * @param {Object} value The value (material and color) of the part to be changed.
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Object} The new update ctx with the desired changes made to the current ctx.
     */
    "onPart" : function(name, value, ctx) {
        console.log(`Trying to change part '${name}' for material '${value["material"]}' and color '${value["color"]}'`);
        if (name !== "shadow") {
            ctx.parts[name] = {
                "material": "leather_dmy",
                "color": "black"
            };
            ctx.messages.concat([["config", "Colors forced to black"]]);
        }
    },

    /**
     * Allows the change of the current context of execution, this logic method is
     * to be called on situation where the initials or engraving values were changed.
     *
     * @param {String} group The name of the group that is going to be changed.
     * @param {String} initials The initials value to be changed.
     * @param {String} engraving The engraving value to be changed.
     * @param {Object} ctx The object containing the customization context
     * to be used for the operation execution.
     * @return {Object} The new update ctx with the desired changes made to the current ctx.
     */
    "onInitials" : function(group, initials, engraving, ctx) {
        console.log(`Trying to change initials to '${initials}' in '${engraving}' for group '${group}'`);

        const blacklist = ["dog", "cat"];
        if (blacklist.includes(initials)) {
            ctx.initials.main.initials = "";
            ctx.messages.concat([["initials", "Blacklisted word detected"]]);
        }

        return ctx;
    }

    /**
     * Allows to extend the set of validations done against a context.
     * One should call the base's `validate_gen` and then append the
     * desired validations by "yielding" the invalidation messages.
     *
     * @param {Object} ctx The object containing the customization context
     * to be used for the validation operation.
     */
    "validateGen" : function*(ctx) {
        super.validate_gen(ctx);

        const initials = ctx.initials.main.initials;
        if (blacklist.indexOf(initials) > -1) {
            yield `The word '${initials}' is blacklisted`;
        }
    }
}

Compatibility levels

Over the years this specification has had the need to evolve over time. In order to keep backwards compatibility between the newest specification version and the existing 3DBs or APIs, some tools of the RIPE ecosystem (ripe-compose, etc) use the concept of compatibility layer to try and adapt or patch the older versions to be retro-compatible.

Level 1

Highest level of compatibility that tries to ensure that all the legacy features and APIs are available for the model.

Level 2

The same level of compatibility as Level 1, excluding: