#VRML_SIM R2022b utf8 # license: Copyright Cyberbotics Ltd. Licensed for use only with Webots. # license url: https://cyberbotics.com/webots_assets_license # A customizable cabinet containing elements (shelves, dynamic doors, and drawers). # The internal cabinet layout is a grid in which the elements can be inserted. # The grid dimension (and so the cabinet size) is defined by the 'rowsHeights' and the 'columnsWidths' fields. # The `layout` field is defining the location and the dimension of the elements into the grid layout, according to the following syntax: # - `layout`: list("[RightSidedDoor|LeftSiderDoor|Drawer|Shelf] (x, y, column span, row span[, mass])" # # The coordinate origin (1,1) of the grid layout is at the bottom left corner. # template language: javascript EXTERNPROTO "CabinetHandle.proto" EXTERNPROTO "webots://projects/appearances/protos/PaintedWood.proto" EXTERNPROTO "webots://projects/objects/solids/protos/SolidBox.proto" EXTERNPROTO "CabinetDoor.proto" EXTERNPROTO "CabinetDrawer.proto" PROTO Cabinet [ field SFVec3f translation 0 0 0 field SFRotation rotation 0 0 1 0 field SFString name "cabinet" field SFFloat depth 0.5 # Defines the depth of the cabinet. field SFFloat innerThickness 0.02 # Defines the inner thickness of the cabinet frame. field SFFloat outerThickness 0.03 # Defines the outer thickness of the cabinet frame. field MFFloat rowsHeights [ # Defines the height of the cabinet rows. 0.24 0.2 0.2 0.4 0.4 ] field MFFloat columnsWidths [ # Defines the width of the cabinet columns. 0.4 0.17 0.17 ] field MFString layout [ # Defines the layout of the cabinet. "RightSidedDoor (1, 4, 1, 2, 1.5)" "LeftSidedDoor (2, 4, 2, 2, 1.5)" "Drawer (3, 3, 1, 1, 1.5)" "Drawer (2, 2, 1, 1, 1.5)" "Drawer (3, 2, 1, 1, 1.5)" "Drawer (1, 1, 3, 1, 3.5)" "Shelf (1, 5, 3, 0)" "Shelf (1, 4, 3, 0)" "Shelf (1, 3, 3, 0)" "Shelf (1, 2, 3, 0)" "Shelf (1, 3, 0, 1)" "Shelf (2, 3, 0, 1)" ] field SFNode handle CabinetHandle {} # Defines the handle of the cabinet. field SFNode primaryAppearance PaintedWood {} # Defines the primary appearance. field SFNode secondaryAppearance PaintedWood {} # Defines the secondary appearance. ] { %< function computeCoordinate (elements, item, dimension) { let from = 0.0; for (let i = 0; i < (item - 1); ++i) if (elements[i] !== undefined) from += elements[i]; let to = 0.0; for (let i = 0; i < item + dimension - 1; ++i) if (elements[i] !== undefined) to += elements[i]; return 0.5 * (from + to); } function computeDimension (elements, item, dimension) { let length = 0.0; for (let i = item - 1; i < item + dimension - 1; ++i) if (elements[i] !== undefined) length += elements[i]; return length; } let innerThickness = fields.innerThickness.value; if (innerThickness <= 0.0) { innerThickness = fields.innerThickness.defaultValue; console.error('\'innerThickness\' must be strictly positive. Value reset to ' + innerThickness + '.'); } let outerThickness = fields.outerThickness.value; if (outerThickness <= 0.0) { outerThickness = fields.outerThickness.defaultValue; console.error('\'outerThickness\' must be strictly positive. Value reset to ' + outerThickness + '.'); } if (2.0 * outerThickness <= innerThickness) { outerThickness = 0.51 * innerThickness; console.error('\'innerThickness\' should be at least twice bigger than \'outerThickness\'. Value reset to ' + outerThickness + '.'); } let depth = fields.depth.value; if (depth <= 0.0) { depth = fields.depth.defaultValue; console.error('\'depth\' must be strictly positive. Value reset to ' + depth + '.'); } if (depth < 2 * outerThickness + 3 * innerThickness) { depth = 2 * outerThickness + 3 * innerThickness; console.error('\'depth\' must be bigger than 2 * \'outerThickness\' + 3 * \'innerThickness\'. Value reset to ' + depth + '.'); } let rowsHeights = fields.rowsHeights.value; const nRows = fields.rowsHeights.value.length; for (let i = 0; i < nRows; ++i) { const height = rowsHeights[i]; if (height < 3 * innerThickness) { rowsHeights[i] = 3 * innerThickness; // minimum size console.error('\'rowsHeights[' + i + ']\' must be bigger than 3 * \'innerThickness\'. Value reset to ' + rowsHeights[i] + '.'); } } let columnsWidths = fields.columnsWidths.value; const nColumns = fields.columnsWidths.value.length; for (let i = 0; i < nColumns; ++i) { const width = columnsWidths[i]; if (width < 3 * innerThickness) { columnsWidths[i] = 3 * innerThickness; // minimum size; console.error('\'columnsWidths[' + i + ']\' must be bigger than 3 * \'innerThickness\'. Value reset to ' + columnsWidths[i] + '.'); } } let size = {x: depth, y: 2 * outerThickness, z: 2 * outerThickness}; for (let i = 0; i < nColumns; ++i) size.y += columnsWidths[i]; for (let i = 0; i < nRows; ++i) size.z += rowsHeights[i]; // in order to avoid object collisions const objectScaleFactor = 0.995; >% Solid { translation IS translation rotation IS rotation children [ SolidBox { # back of the cabinet frame translation %<= 0.5 * outerThickness >% 0 %<= 0.5 * size.z >% name "back box" size %<= outerThickness >% %<= size.y >% %<= size.z - 2.0 * outerThickness >% appearance IS primaryAppearance } SolidBox { # left side of the cabinet frame translation %<= 0.5 * outerThickness + 0.5 * size.x >% %<= 0.5 * size.y - 0.5 * outerThickness >% %<= 0.5 * size.z >% name "left box" size %<= size.x - outerThickness >% %<= outerThickness >% %<= size.z - 2.0 * outerThickness >% appearance IS primaryAppearance } SolidBox { # right side of the cabinet frame translation %<= 0.5 * outerThickness + 0.5 * size.x >% %<= -0.5 * size.y + 0.5 * outerThickness >% %<= 0.5 * size.z >% name "right box" size %<= size.x - outerThickness >% %<= outerThickness >% %<= size.z - 2.0 * outerThickness >% appearance IS primaryAppearance } SolidBox { # top side of the cabinet frame translation %<= 0.5 * size.x >% 0 %<= size.z - 0.5 * outerThickness>% name "top box" size %<= size.x >% %<= size.y >% %<= outerThickness >% appearance IS primaryAppearance } SolidBox { # bottom side of the cabinet frame translation %<= 0.5 * size.x >% 0 %<= 0.5 * outerThickness >% name "bottom box" size %<= size.x >% %<= size.y >% %<= outerThickness >% appearance IS primaryAppearance } # parse layout %< for (let i = 0; i < fields.layout.value.length; ++i) { >% %< const layout = fields.layout.value[i]; let data = layout.match(/([^", ( ) %s"]+)/g); for (let j = 1; j < data.length; ++j) { data[j] = Number(data[j]); if (data[j] < 0) { data[j] = 0.1; console.error('\'layout[' + i + '][' + j + ']\' must be positive. Value reset to ' + data[j] + '.'); } } >% %< if (data.length >= 5) { >% %< if (data[1] > 0 && data[1] <= nColumns && data[2] > 0 && data[2] <= nRows && data[3] >= 0 && data[1] + data[3] - 1 <= nColumns && data[4] >= 0 && data[2] + data[4] - 1 <= nRows) { >% %< if (data[0] === 'RightSidedDoor' || data[0] === 'LeftSidedDoor' || data[0] === 'Drawer') { >% %< if (data[0] === 'RightSidedDoor') { >% CabinetDoor { name %<= '"door ' + i + '"' >% rightSided TRUE %< } else if (data[0] === 'LeftSidedDoor') { >% CabinetDoor { name %<= '"door ' + i + '"' >% rightSided FALSE %< } else if (data[0] === 'Drawer') { >% CabinetDrawer { name %<= '"drawer ' + i + '"' >% %< } >% translation %<= size.x >% %<= computeCoordinate(columnsWidths, data[1], data[3]) - 0.5 * size.y + outerThickness >% %<= computeCoordinate(rowsHeights, data[2], data[4]) + outerThickness >% size %<= objectScaleFactor * (size.x - outerThickness) >% %<= objectScaleFactor * computeDimension(columnsWidths, data[1], data[3]) >% %<= objectScaleFactor * computeDimension(rowsHeights, data[2], data[4]) >% %< if (data[5] !== undefined && data[5] > 0.0) { >% mass %<= data[5] >% %< } >% thickness IS innerThickness handle IS handle %< if (data[0] === 'Drawer') { >% primaryAppearance IS primaryAppearance %< } >% secondaryAppearance IS secondaryAppearance } %< } else if (data[0] === 'Shelf') { >% %< const horizontalOffset = data[3] === 0 ? 1 : 0; const depthFightingOffset = data[3] < data[4] ? 0.0001 : 0; >% SolidBox { name %<= '"box ' + i + '"' >% appearance IS primaryAppearance translation %<= 0.5 * size.x + 0.5 * (outerThickness - innerThickness) + depthFightingOffset >% %<= computeCoordinate(columnsWidths, horizontalOffset + data[1], data[3]) - 0.5 * size.y + outerThickness + depthFightingOffset >% %<= computeCoordinate(rowsHeights, data[2], data[4]) + outerThickness + depthFightingOffset >% size %<= objectScaleFactor * (size.x - outerThickness - innerThickness) >% %<= innerThickness + computeDimension(columnsWidths, data[1], data[3]) >% %<= innerThickness + computeDimension(rowsHeights, data[2], data[4]) >% } %< } else { >% %< console.error('\'layout[' + i + ']\': unknown object: ' + data[0] + '.'); >% %< } >% %< } else { >% %< console.error('\'layout[' + i + ']\': invalid position or dimension.'); >% %< } >% %< } else { >% %< console.error('\'layout[' + i + ']\': invalid layout.'); >% %< } >% %< } >% ] name IS name model "cabinet" } }