From 613e492a9619457742ab019664b0364c03306c60 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Tue, 2 Jul 2024 23:42:02 -0700 Subject: [PATCH 01/37] allow programatic determination of base node model ancestry Signed-off-by: CoolSpy3 --- resources/nodes/Accelerometer.wrl | 1 + resources/nodes/Altimeter.wrl | 1 + resources/nodes/BallJoint.wrl | 1 + resources/nodes/BallJointParameters.wrl | 1 + resources/nodes/Billboard.wrl | 1 + resources/nodes/Camera.wrl | 1 + resources/nodes/Charger.wrl | 1 + resources/nodes/Compass.wrl | 1 + resources/nodes/Connector.wrl | 1 + resources/nodes/Display.wrl | 1 + resources/nodes/DistanceSensor.wrl | 1 + resources/nodes/Emitter.wrl | 1 + resources/nodes/Fluid.wrl | 1 + resources/nodes/GPS.wrl | 1 + resources/nodes/Gyro.wrl | 1 + resources/nodes/Hinge2Joint.wrl | 1 + resources/nodes/HingeJointParameters.wrl | 1 + resources/nodes/InertialUnit.wrl | 1 + resources/nodes/LED.wrl | 1 + resources/nodes/Lidar.wrl | 1 + resources/nodes/LightSensor.wrl | 1 + resources/nodes/Microphone.wrl | 1 + resources/nodes/Pen.wrl | 1 + resources/nodes/Pose.wrl | 1 + resources/nodes/PositionSensor.wrl | 1 + resources/nodes/Propeller.wrl | 1 + resources/nodes/Radar.wrl | 1 + resources/nodes/Radio.wrl | 1 + resources/nodes/RangeFinder.wrl | 1 + resources/nodes/Receiver.wrl | 1 + resources/nodes/Robot.wrl | 1 + resources/nodes/Solid.wrl | 1 + resources/nodes/Speaker.wrl | 1 + resources/nodes/TouchSensor.wrl | 1 + resources/nodes/Track.wrl | 1 + resources/nodes/TrackWheel.wrl | 1 + resources/nodes/Transform.wrl | 1 + resources/nodes/VacuumGripper.wrl | 1 + src/webots/vrml/WbNodeModel.cpp | 9 ++++++++- src/webots/vrml/WbNodeModel.hpp | 6 ++++++ src/webots/vrml/WbTokenizer.cpp | 11 +++++++++++ src/webots/vrml/WbTokenizer.hpp | 3 +++ 42 files changed, 66 insertions(+), 1 deletion(-) diff --git a/resources/nodes/Accelerometer.wrl b/resources/nodes/Accelerometer.wrl index 86e0fd29865..8bf495bd30f 100644 --- a/resources/nodes/Accelerometer.wrl +++ b/resources/nodes/Accelerometer.wrl @@ -2,6 +2,7 @@ # simulation. The acceleration is measured along the 3 axes (X, Y and Z) and is # expressed in m/s^2. It is mostly used to measure the direction of the # gravity, but can be used for many other purposes. +# parent: Solid Accelerometer { #fields that inherit from the Solid node: diff --git a/resources/nodes/Altimeter.wrl b/resources/nodes/Altimeter.wrl index 01b806520a2..47574edc71a 100644 --- a/resources/nodes/Altimeter.wrl +++ b/resources/nodes/Altimeter.wrl @@ -1,4 +1,5 @@ # The Altimeter node can be used to determine the global altitude of a robot or of a robot part. +# parent: Solid Altimeter { #fields that inherit from the Solid node: diff --git a/resources/nodes/BallJoint.wrl b/resources/nodes/BallJoint.wrl index 1a6bf4ef9eb..eadf9602170 100644 --- a/resources/nodes/BallJoint.wrl +++ b/resources/nodes/BallJoint.wrl @@ -1,4 +1,5 @@ # A BallJoint node can be used to simulate a rotating motion with 3 DOF (ball and socket). +# parent: Hinge2Joint BallJoint { field SFNode jointParameters NULL # BallJointParameters specifying the joint anchor and spring and damper constants, minStop, maxStop related to the first axis diff --git a/resources/nodes/BallJointParameters.wrl b/resources/nodes/BallJointParameters.wrl index 0bcc380eb1c..c8b53346612 100644 --- a/resources/nodes/BallJointParameters.wrl +++ b/resources/nodes/BallJointParameters.wrl @@ -1,4 +1,5 @@ # The BallJointParamaters node defines the parameters of a BallJoint node. +# parent: JointParameters BallJointParameters { field SFFloat position 0 # current position (rad) diff --git a/resources/nodes/Billboard.wrl b/resources/nodes/Billboard.wrl index bb824ac60a2..d7ca5797d52 100644 --- a/resources/nodes/Billboard.wrl +++ b/resources/nodes/Billboard.wrl @@ -1,5 +1,6 @@ # A Billboard node contains children nodes that rotate and translate automatically to face the viewpoint. # It is otherwise similar to a Group node. +# parent: Group Billboard { #field that inherits from the Group node: diff --git a/resources/nodes/Camera.wrl b/resources/nodes/Camera.wrl index 504b3086cde..7664f285206 100644 --- a/resources/nodes/Camera.wrl +++ b/resources/nodes/Camera.wrl @@ -1,4 +1,5 @@ # The Camera node is used to model an on-board camera. +# parent: Solid Camera { #fields that inherit from the Solid node: diff --git a/resources/nodes/Charger.wrl b/resources/nodes/Charger.wrl index 13b1df22874..53670c8d38b 100644 --- a/resources/nodes/Charger.wrl +++ b/resources/nodes/Charger.wrl @@ -1,5 +1,6 @@ # The Charger node is used to model a special kind of battery charger for the robots. # When a robot gets close to a Charger, the robot's battery gets recharged. +# parent: Solid Charger { #fields that inherit from the Solid node: diff --git a/resources/nodes/Compass.wrl b/resources/nodes/Compass.wrl index 61997be077a..6b79161c369 100644 --- a/resources/nodes/Compass.wrl +++ b/resources/nodes/Compass.wrl @@ -1,5 +1,6 @@ # A Compass node can be used to simulate 1, 2 and 3-axis digital compasses. # It indicates the direction of the simulated magnetic north which is specified in the WorldInfo node. +# parent: Solid Compass { #fields that inherit from the Solid node: diff --git a/resources/nodes/Connector.wrl b/resources/nodes/Connector.wrl index 9478f7c57ba..289b431fe9b 100644 --- a/resources/nodes/Connector.wrl +++ b/resources/nodes/Connector.wrl @@ -1,6 +1,7 @@ # Connector nodes are used to simulate mechanical docking systems, or any other type of device that # can dynamically create a rigid link with a similar device. # The physical connection between two Connectors can be created and destroyed at run time by the robot controller program. +# parent: Solid Connector { #fields that inherit from the Solid node: diff --git a/resources/nodes/Display.wrl b/resources/nodes/Display.wrl index 8de01d2ba7c..af3b0e45cb5 100644 --- a/resources/nodes/Display.wrl +++ b/resources/nodes/Display.wrl @@ -4,6 +4,7 @@ # It can model an embedded screen or it can display any graphical # information such as graphs, text, robot trajectory, filtered camera # images and so on. +# parent: Solid Display { #fields that inherit from the Solid node: diff --git a/resources/nodes/DistanceSensor.wrl b/resources/nodes/DistanceSensor.wrl index 676feb95fe9..31be85f211a 100644 --- a/resources/nodes/DistanceSensor.wrl +++ b/resources/nodes/DistanceSensor.wrl @@ -1,6 +1,7 @@ # The DistanceSensor node can be used to model an ultrasound sonar, an infra-red sensor, # a single-ray laser or any type of device that measures the distance to objects. # To model a Lidar sensor, you should rather use a Lidar node. +# parent: Solid DistanceSensor { #fields that inherit from the Solid node: diff --git a/resources/nodes/Emitter.wrl b/resources/nodes/Emitter.wrl index 535ed39fe4e..afa0ebd6199 100644 --- a/resources/nodes/Emitter.wrl +++ b/resources/nodes/Emitter.wrl @@ -1,6 +1,7 @@ # The Emitter node is used to model a radio, or infra-red emitter. # It can be used to send data packets to Receiver nodes (onboard other robots). # An Emitter cannot receive data: bidirectional communication requires two Emitter/Receiver pairs. +# parent: Solid Emitter { #fields that inherit from the Solid node: diff --git a/resources/nodes/Fluid.wrl b/resources/nodes/Fluid.wrl index 8d426d8f0e1..a56c2535d02 100644 --- a/resources/nodes/Fluid.wrl +++ b/resources/nodes/Fluid.wrl @@ -1,4 +1,5 @@ # A Fluid node can be used to represent a collection of fluid volumes where hydrostatic and hydrodynamic forces apply. +# parent: Pose Fluid { #fields that inherit from the Pose node: diff --git a/resources/nodes/GPS.wrl b/resources/nodes/GPS.wrl index 8f0f46951a9..39bbea3702a 100644 --- a/resources/nodes/GPS.wrl +++ b/resources/nodes/GPS.wrl @@ -1,4 +1,5 @@ # The GPS node can be used to determine the global position of a robot or of a robot part. +# parent: Solid GPS { #fields that inherit from the Solid node: diff --git a/resources/nodes/Gyro.wrl b/resources/nodes/Gyro.wrl index c1f14f5a3e5..883db4d5282 100644 --- a/resources/nodes/Gyro.wrl +++ b/resources/nodes/Gyro.wrl @@ -1,5 +1,6 @@ # A Gyro node measures the angular velocity about 3 orthogonal axes (X, Y and Z). # The output is in rad/s. The Gyro node is mostly used for balance control. +# parent: Solid Gyro { #fields that inherit from the Solid node: diff --git a/resources/nodes/Hinge2Joint.wrl b/resources/nodes/Hinge2Joint.wrl index 6e87d208921..362a0ad6fbc 100644 --- a/resources/nodes/Hinge2Joint.wrl +++ b/resources/nodes/Hinge2Joint.wrl @@ -1,6 +1,7 @@ # A Hinge2Joint can be used to simulate a combination of two rotating motions along axes which intersect. # It is equivalent to two HingeJoint nodes but it spares the creation of an intermediate solid and is therefore more stable. # Spring and damping behavior can be specified. +# parent: HingeJoint Hinge2Joint { field SFNode jointParameters NULL # HingeJointParameters specifying anchor, axis, spring and damper constants, minStop, maxStop, suspension diff --git a/resources/nodes/HingeJointParameters.wrl b/resources/nodes/HingeJointParameters.wrl index c42c417c3a4..c6a0c87bbbb 100644 --- a/resources/nodes/HingeJointParameters.wrl +++ b/resources/nodes/HingeJointParameters.wrl @@ -1,4 +1,5 @@ # A HingeJointParameters defines the parameters of a HingeJoint node. +# parent: JointParameters HingeJointParameters { field SFFloat position 0 # current position (m or rad) diff --git a/resources/nodes/InertialUnit.wrl b/resources/nodes/InertialUnit.wrl index 02a9f91e9c0..7cc3aca09ac 100644 --- a/resources/nodes/InertialUnit.wrl +++ b/resources/nodes/InertialUnit.wrl @@ -1,6 +1,7 @@ # The InertialUnit node simulates an Inertial Measurement Unit (IMU). # The InertialUnit node computes and returns the roll, pitch and yaw angles of the # robot with respect to the global coordinate system defined in the WorldInfo node. +# parent: Solid InertialUnit { #fields that inherit from the Solid node: diff --git a/resources/nodes/LED.wrl b/resources/nodes/LED.wrl index f79000b5acd..3dfab69dbd8 100644 --- a/resources/nodes/LED.wrl +++ b/resources/nodes/LED.wrl @@ -1,4 +1,5 @@ # The LED node can be used to model a light emitting diode (LED) that can be controlled by the robot. +# parent: Solid LED { #fields that inherit from the Solid node: diff --git a/resources/nodes/Lidar.wrl b/resources/nodes/Lidar.wrl index f460f2a517e..6ee95adfdb5 100644 --- a/resources/nodes/Lidar.wrl +++ b/resources/nodes/Lidar.wrl @@ -1,5 +1,6 @@ # The Lidar node is used to model an on-board lidar. # A lidar is used to measure the distance to obstacles. +# parent: Solid Lidar { #fields that inherit from the Solid node: diff --git a/resources/nodes/LightSensor.wrl b/resources/nodes/LightSensor.wrl index 7451194410b..702e46c4eb5 100644 --- a/resources/nodes/LightSensor.wrl +++ b/resources/nodes/LightSensor.wrl @@ -1,6 +1,7 @@ # A LightSensor node can be used to model a phototransistor, a photodiode or any type # of device that measures the irradiance of light on its surface. # A LightSensor node detects the light emitted by PointLight, SpotLight and DirectionalLight nodes. +# parent: Solid LightSensor { # fields inherited from the Solid node: diff --git a/resources/nodes/Microphone.wrl b/resources/nodes/Microphone.wrl index a7a4df727f7..a17826356fa 100644 --- a/resources/nodes/Microphone.wrl +++ b/resources/nodes/Microphone.wrl @@ -1,3 +1,4 @@ +# parent: Solid Microphone { # fields that inherit from the Solid node: w3dField SFVec3f translation 0 0 0 diff --git a/resources/nodes/Pen.wrl b/resources/nodes/Pen.wrl index 025d66716f0..12f522ce40a 100644 --- a/resources/nodes/Pen.wrl +++ b/resources/nodes/Pen.wrl @@ -1,5 +1,6 @@ # A Pen node can be used to model a pen attached to a mobile robot. # It can draw the trajectory of the robot on a textured ground. +# parent: Solid Pen { #fields that inherit from the Solid node: diff --git a/resources/nodes/Pose.wrl b/resources/nodes/Pose.wrl index 66d61f806e0..7465b4d4bb3 100644 --- a/resources/nodes/Pose.wrl +++ b/resources/nodes/Pose.wrl @@ -1,5 +1,6 @@ # The Pose node is a grouping node that defines a coordinate system for its children that is # relative to the coordinate system of its parent. +# parent: Group Pose { #fields specific to the AbstractPose node: diff --git a/resources/nodes/PositionSensor.wrl b/resources/nodes/PositionSensor.wrl index 11b96bceb2e..07a7768d8a0 100644 --- a/resources/nodes/PositionSensor.wrl +++ b/resources/nodes/PositionSensor.wrl @@ -1,4 +1,5 @@ # A PositionSensor allows a robot controller to read the position of a joint with respect to its main axis. +# parent: Solid PositionSensor { field SFString name "position sensor" # used by wb_robot_get_device() diff --git a/resources/nodes/Propeller.wrl b/resources/nodes/Propeller.wrl index 7de1ddc67bb..2c21c71ca53 100644 --- a/resources/nodes/Propeller.wrl +++ b/resources/nodes/Propeller.wrl @@ -1,5 +1,6 @@ # The Propeller node is used to model a motorized helix propeller. # It can be used to propel underwater robots, flying robots, floating robots or even wheeled robots. +# parent: Solid Propeller { field SFVec3f shaftAxis 1 0 0 # thrust direction (m) diff --git a/resources/nodes/Radar.wrl b/resources/nodes/Radar.wrl index 1966c5509f7..d0c2d8908a4 100644 --- a/resources/nodes/Radar.wrl +++ b/resources/nodes/Radar.wrl @@ -1,4 +1,5 @@ # The Radar node is used to model a radar device, commonly found in automobiles. +# parent: Solid Radar { #fields that inherit from the Solid node: diff --git a/resources/nodes/Radio.wrl b/resources/nodes/Radio.wrl index 2daa9a566a0..b07fcc508b8 100644 --- a/resources/nodes/Radio.wrl +++ b/resources/nodes/Radio.wrl @@ -1,4 +1,5 @@ # Experimental Radio node. +# parent: Solid Radio { #fields that inherit from the Solid node: diff --git a/resources/nodes/RangeFinder.wrl b/resources/nodes/RangeFinder.wrl index 5651bb3bc9c..ae2620dddae 100644 --- a/resources/nodes/RangeFinder.wrl +++ b/resources/nodes/RangeFinder.wrl @@ -1,5 +1,6 @@ # The RangeFinder node is used to model an on-board range finder. # A range finder is used to measure the distance to obstacles. +# parent: Solid RangeFinder { #fields that inherit from the Solid node: diff --git a/resources/nodes/Receiver.wrl b/resources/nodes/Receiver.wrl index 381f5b81a58..713d83cf58c 100644 --- a/resources/nodes/Receiver.wrl +++ b/resources/nodes/Receiver.wrl @@ -1,6 +1,7 @@ # A Receiver node models a radio or infra-red receiver. # It can be used to receive data packets emitted by Emitter nodes (onboard other robots). # A Receiver cannot emit data: bidirectional communication requires two Emitter/Receiver pairs. +# parent: Solid Receiver { #fields that inherit from the Solid node: diff --git a/resources/nodes/Robot.wrl b/resources/nodes/Robot.wrl index 7a1ba009e4e..c971af28c0a 100644 --- a/resources/nodes/Robot.wrl +++ b/resources/nodes/Robot.wrl @@ -1,4 +1,5 @@ # The Robot node is a generic type of robot. +# parent: Solid Robot { #fields that inherit from the Solid node: diff --git a/resources/nodes/Solid.wrl b/resources/nodes/Solid.wrl index 332b50b4bef..45f1f7f05fc 100644 --- a/resources/nodes/Solid.wrl +++ b/resources/nodes/Solid.wrl @@ -1,6 +1,7 @@ # A Solid node can be used to represent objects in the simulated environment (e.g. obstacles, walls, ground, robot parts, etc.). # Solid nodes can be collision detected (boundingObject) and therefore can prevent objects from intersecting. # In addition, Solid nodes can have an optional Physics node that allow them to be simulated with the physics engine. +# parent: Pose Solid { #fields that inherit from the Pose node: diff --git a/resources/nodes/Speaker.wrl b/resources/nodes/Speaker.wrl index 0f925047b9a..31b82f98d65 100644 --- a/resources/nodes/Speaker.wrl +++ b/resources/nodes/Speaker.wrl @@ -1,6 +1,7 @@ # The Speaker node is used to model a loudspeaker device. # It can be used to playback wav sound files as well as text-to-speech. # The sounds are localized in the 3D space and rendered at the main viewpoint in stereo. +# parent: Solid Speaker { #fields that inherit from the Solid node: diff --git a/resources/nodes/TouchSensor.wrl b/resources/nodes/TouchSensor.wrl index d45a9e5ccec..810f20337bf 100644 --- a/resources/nodes/TouchSensor.wrl +++ b/resources/nodes/TouchSensor.wrl @@ -1,6 +1,7 @@ # A TouchSensor can be used to measure contact force ("force", "force-3d") or simply detect collisions ("bumper"). # It is critical that 'boundingObject' of a TouchSensor is defined and placed appropriately. # Refer to the Webots reference manual for more information on this. +# parent: Solid TouchSensor { #models a bumper, button, cat whisker, force sensor etc. #fields that inherit from the Solid node: diff --git a/resources/nodes/Track.wrl b/resources/nodes/Track.wrl index ccff593c54b..77a8f6c4b3f 100644 --- a/resources/nodes/Track.wrl +++ b/resources/nodes/Track.wrl @@ -1,4 +1,5 @@ # The Track node can be used to simulate tracks of tank robots or conveyor belts. +# parent: Solid Track { #fields that inherit from the Solid node: diff --git a/resources/nodes/TrackWheel.wrl b/resources/nodes/TrackWheel.wrl index 7422c3ad2cb..c927d9b130e 100644 --- a/resources/nodes/TrackWheel.wrl +++ b/resources/nodes/TrackWheel.wrl @@ -1,4 +1,5 @@ # A TrackWheel node can be used to simulate a wheel that it is part of a track system. +# parent: Group TrackWheel { #fields specific to the TrackWheel node: diff --git a/resources/nodes/Transform.wrl b/resources/nodes/Transform.wrl index ababcd3a51e..779ff528904 100644 --- a/resources/nodes/Transform.wrl +++ b/resources/nodes/Transform.wrl @@ -1,6 +1,7 @@ # The Transform node is a grouping node that defines a coordinate system for its children that is # relative to the coordinate system of its parent. # The 'scale' field of a Transform node can be adjusted only in a graphical context and not in a 'boundingObject' context. +# parent: Pose Transform { #fields specific to the AbstractTransform node: diff --git a/resources/nodes/VacuumGripper.wrl b/resources/nodes/VacuumGripper.wrl index 0d25e55475a..05950fe95d0 100644 --- a/resources/nodes/VacuumGripper.wrl +++ b/resources/nodes/VacuumGripper.wrl @@ -1,5 +1,6 @@ # VacuumGripper nodes are used to simulate vacuum suction systems. # The physical connection with a Solid can be created and destroyed at run time by the robot controller program. +# parent: Solid VacuumGripper { #fields that inherit from the Solid node: diff --git a/src/webots/vrml/WbNodeModel.cpp b/src/webots/vrml/WbNodeModel.cpp index c8aabe7e287..ae51f2da52c 100644 --- a/src/webots/vrml/WbNodeModel.cpp +++ b/src/webots/vrml/WbNodeModel.cpp @@ -34,7 +34,7 @@ void WbNodeModel::cleanup() { delete it.next().value(); } -WbNodeModel::WbNodeModel(WbTokenizer *tokenizer) : mInfo(tokenizer->info()), mName(tokenizer->nextWord()) { +WbNodeModel::WbNodeModel(WbTokenizer *tokenizer) : mInfo(tokenizer->info()), mName(tokenizer->nextWord()), mParentName(tokenizer->parent()) { tokenizer->skipToken("{"); while (tokenizer->peekWord() != "}") { @@ -78,6 +78,13 @@ void WbNodeModel::readAllModels() { cModels.insert(model->name(), model); } + // Now that all the models are loaded, populate the ancestory tree + foreach (QString baseModelName, baseModelNames()) { + WbNodeModel *baseModel = findModel(baseModelName); + if (baseModel) + baseModel->mParentModel = findModel(baseModel->mParentName); + } + qAddPostRoutine(WbNodeModel::cleanup); } diff --git a/src/webots/vrml/WbNodeModel.hpp b/src/webots/vrml/WbNodeModel.hpp index a2ee4a4fac1..625910cef90 100644 --- a/src/webots/vrml/WbNodeModel.hpp +++ b/src/webots/vrml/WbNodeModel.hpp @@ -51,6 +51,10 @@ class WbNodeModel { const QList &fieldModels() const { return mFieldModels; } QStringList fieldNames(); + // parent nodes (only defined for base nodes. For proto nodes, see WbProtoModel::ancestorProtoName/ancestorProtoNameModel/baseType) + const QString parentName() const { return mParentName; }; + const WbNodeModel *parentModel() const { return mParentModel; }; + QStringList documentationBookAndPage() const { return QStringList() << "reference" << mName.toLower(); } private: @@ -60,6 +64,8 @@ class WbNodeModel { QString mInfo; QString mName; QList mFieldModels; + QString mParentName; + WbNodeModel *mParentModel; static WbNodeModel *readModel(const QString &fileName); static void readAllModels(); diff --git a/src/webots/vrml/WbTokenizer.cpp b/src/webots/vrml/WbTokenizer.cpp index 8b35fc9096e..8685c2ddffc 100644 --- a/src/webots/vrml/WbTokenizer.cpp +++ b/src/webots/vrml/WbTokenizer.cpp @@ -529,6 +529,17 @@ const QString WbTokenizer::documentationUrl() const { return QString(); } +const QString WbTokenizer::parent() const { + const QStringList lines = mInfo.split("\n"); + foreach (QString line, lines) { + if (line.startsWith("parent:")) { + line.remove("parent:"); + return line.trimmed(); + } + } + return QString(); +} + void WbTokenizer::reportError(const QString &message, int line, int column) const { const QString prefix = mFileName.isEmpty() ? mReferralFile : mFileName; if (prefix.isEmpty()) diff --git a/src/webots/vrml/WbTokenizer.hpp b/src/webots/vrml/WbTokenizer.hpp index e5b20145c3c..ee67e6446d7 100644 --- a/src/webots/vrml/WbTokenizer.hpp +++ b/src/webots/vrml/WbTokenizer.hpp @@ -61,6 +61,9 @@ class WbTokenizer { // returns the documentation URL stored as (# license url: string) comments in the file header const QString documentationUrl() const; + // returns the parent node type stored as (# parent: string) comments in the file header + const QString parent() const; + // returns file version found in file header const WbVersion &fileVersion() const { return mFileVersion; } From 1a807d665835839a15869d893bc70a5a8063fa46 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Tue, 2 Jul 2024 23:43:59 -0700 Subject: [PATCH 02/37] consider node ancestry in proto type restrictions Signed-off-by: CoolSpy3 --- src/webots/vrml/WbFieldModel.cpp | 18 ++++++++++++++---- src/webots/vrml/WbFieldModel.hpp | 4 ++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/webots/vrml/WbFieldModel.cpp b/src/webots/vrml/WbFieldModel.cpp index bfdb253c4f8..2fde876210f 100644 --- a/src/webots/vrml/WbFieldModel.cpp +++ b/src/webots/vrml/WbFieldModel.cpp @@ -212,8 +212,7 @@ bool WbFieldModel::isValueAccepted(const WbValue *value, int *refusedIndex) cons assert(mfNode); foreach (const WbVariant acceptedVariant, mAcceptedValues) { const WbNode *nodeAccepted = acceptedVariant.toNode(); - if (nodeAccepted && (mfNode->item(i)->nodeModelName() == nodeAccepted->modelName() || - mfNode->item(i)->modelName() == nodeAccepted->modelName())) { + if (nodeAccepted && (nodeAccepted->isProtoInstance() ? isProtoNodeTypeAccepted(nodeAccepted->modelName(), mfNode->item(i)->proto()) : isBaseNodeTypeAccepted(nodeAccepted->nodeModelName(), mfNode->item(i)->model()))) { accepted = true; break; } @@ -242,8 +241,7 @@ bool WbFieldModel::isValueAccepted(const WbValue *value, int *refusedIndex) cons return true; const WbNode *nodeAccepted = acceptedVariant.toNode(); assert(nodeAccepted); - if (sfNode->value()->nodeModelName() == nodeAccepted->modelName() || - sfNode->value()->modelName() == nodeAccepted->modelName()) + if (nodeAccepted->isProtoInstance() ? isProtoNodeTypeAccepted(nodeAccepted->modelName(), sfNode->value()->proto()) : isBaseNodeTypeAccepted(nodeAccepted->nodeModelName(), sfNode->value()->model())) return true; } else if (singleValue->variantValue() == acceptedVariant) return true; @@ -253,6 +251,18 @@ bool WbFieldModel::isValueAccepted(const WbValue *value, int *refusedIndex) cons return false; } +bool WbFieldModel::isBaseNodeTypeAccepted(const QString &expectedType, const WbNodeModel *actualType) const { + if (!actualType) + return false; + return expectedType == actualType->name() || isBaseNodeTypeAccepted(expectedType, actualType->parentModel()); +} + +bool WbFieldModel::isProtoNodeTypeAccepted(const QString &expectedType, const WbProtoModel *actualType) const { + if (!actualType) + return false; + return expectedType == actualType->name() || isProtoNodeTypeAccepted(expectedType, actualType->ancestorProtoModel()); +} + bool WbFieldModel::isMultiple() const { return dynamic_cast(mDefaultValue); } diff --git a/src/webots/vrml/WbFieldModel.hpp b/src/webots/vrml/WbFieldModel.hpp index 907a9619404..91917c0b6e7 100644 --- a/src/webots/vrml/WbFieldModel.hpp +++ b/src/webots/vrml/WbFieldModel.hpp @@ -21,6 +21,8 @@ // #include +#include +#include #include #include @@ -53,6 +55,8 @@ class WbFieldModel { // accepted values bool isValueAccepted(const WbValue *value, int *refusedIndex) const; + bool isBaseNodeTypeAccepted(const QString &expectedType, const WbNodeModel *actualType) const; + bool isProtoNodeTypeAccepted(const QString &expectedType, const WbProtoModel *actualType) const; bool hasRestrictedValues() const { return !mAcceptedValues.isEmpty(); } const QList acceptedValues() const { return mAcceptedValues; } From 21b26cd9e5346567341f00248d15c3a184311521 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 00:17:19 -0700 Subject: [PATCH 03/37] update documentation --- docs/reference/proto-definition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/proto-definition.md b/docs/reference/proto-definition.md index 3323b19b38d..2a60e85e0cd 100644 --- a/docs/reference/proto-definition.md +++ b/docs/reference/proto-definition.md @@ -77,11 +77,11 @@ PROTO MyProto [ field SFString name "my proto" field SFColor{0 0 0, 0.5 0.5 0.5, 1 1 1} color 0.5 0.5 0.5 field SFNode physics NULL - field MFNode{Solid{}, Pose{}} extensionSlot [] + field MFNode{Solid{}, Transform{}} extensionSlot [] ] ``` -In this example, the `color` field value can only be `0 0 0`, `0.5 0.5 0.5` or `1 1 1` and the `extensionSlot` field can only accept [Solid](../reference/solid.md) and [Pose](../reference/pose.md) nodes. +In this example, the `color` field value can only be `0 0 0`, `0.5 0.5 0.5` or `1 1 1` and the `extensionSlot` field can only accept [Solid](../reference/solid.md), [Transform](../reference/transform.md) nodes, and nodes that inherit from them. ### IS Statements From 2c9a21cf6afedfd93b5a69a9dda3b22a2727e341 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 00:37:22 -0700 Subject: [PATCH 04/37] update test suite --- .../protos/DerivedApperanceProto.prot | 12 ++++++ tests/parser/expected_results.txt | 3 +- .../protos/ProtoRestrictedFieldValues.proto | 4 +- .../worlds/proto_not_allowed_field_value.wbt | 4 +- ...proto_not_allowed_mf_field_value_base.wbt} | 3 +- ...proto_not_allowed_mf_field_value_proto.wbt | 41 +++++++++++++++++++ 6 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 tests/manual_tests/protos/DerivedApperanceProto.prot rename tests/parser/worlds/{proto_not_allowed_mf_field_value.wbt => proto_not_allowed_mf_field_value_base.wbt} (86%) create mode 100644 tests/parser/worlds/proto_not_allowed_mf_field_value_proto.wbt diff --git a/tests/manual_tests/protos/DerivedApperanceProto.prot b/tests/manual_tests/protos/DerivedApperanceProto.prot new file mode 100644 index 00000000000..2230d5a9cf1 --- /dev/null +++ b/tests/manual_tests/protos/DerivedApperanceProto.prot @@ -0,0 +1,12 @@ +#VRML_SIM R2024a utf8 + +EXTERNPROTO "webots://tests/manual_tests/protos/AppearanceProto.proto" + +PROTO DerivedAppearanceProto [ + field SFColor color 0 0 0 +] +{ + AppearanceProto { + color IS color + } +} diff --git a/tests/parser/expected_results.txt b/tests/parser/expected_results.txt index 4a014a4fab4..382a4988612 100644 --- a/tests/parser/expected_results.txt +++ b/tests/parser/expected_results.txt @@ -25,7 +25,8 @@ parser/worlds/proto_missing_ending_curly_bracket.wbt "Expected field name or '}' parser/worlds/proto_missing_item_value_in_field.wbt "Expected floating point value, found 'field'." parser/worlds/proto_nested_parameter.wbt VOID parser/worlds/proto_not_allowed_field_value.wbt "Invalid 'translation' changed to 0 0 0. The value should be in the list: {0 0 0}." -parser/worlds/proto_not_allowed_mf_field_value.wbt "Invalid 'Pose' removed from 'extensionSlot' field. The values should be in the list: {Solid, Group}." +parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Transform' removed from 'extensionSlot' field. The values should be in the list: {Solid, AppearanceProto}." +parser/worlds/proto_not_allowed_mf_field_value_proto.wbt "Invalid 'Parameter' removed from 'extensionSlot' field. The values should be in the list: {Solid, AppearanceProto}." parser/worlds/proto_parameter_missing_ending_curly_bracket.wbt "Expected field name or '}', found ']'." parser/worlds/proto_typo_in_alias.wbt "PROTO parameter 'rotation' has no matching IS field." parser/worlds/proto_typo_in_is_keyword.wbt "Expected floating point value, found 'ISNOT'." diff --git a/tests/parser/protos/ProtoRestrictedFieldValues.proto b/tests/parser/protos/ProtoRestrictedFieldValues.proto index 6d4fd73b9b2..90745919593 100644 --- a/tests/parser/protos/ProtoRestrictedFieldValues.proto +++ b/tests/parser/protos/ProtoRestrictedFieldValues.proto @@ -1,10 +1,12 @@ #VRML_SIM R2024a utf8 +EXTERNPROTO "webots://tests/manual_tests/protos/AppearanceProto.proto" + PROTO ProtoRestrictedFieldValues [ field SFRotation{0 1 0 0, 0 0 1 0, 0 1 0 1.5708} rotation 0 0 1 0 field SFVec3f{0 0 0} translation 0 0 0 - field MFNode{Solid{}, Group{}} extensionSlot [] + field MFNode{Solid{}, AppearanceProto{}} extensionSlot [] ] { Solid { diff --git a/tests/parser/worlds/proto_not_allowed_field_value.wbt b/tests/parser/worlds/proto_not_allowed_field_value.wbt index 0d03bcef442..13da759e008 100644 --- a/tests/parser/worlds/proto_not_allowed_field_value.wbt +++ b/tests/parser/worlds/proto_not_allowed_field_value.wbt @@ -30,15 +30,13 @@ ProtoRestrictedFieldValues { rotation 0 1 0 1.5708 translation 0 1 0 extensionSlot [ - Group { + Solid { children [ Solid { name "solid(0)" } ] } - Solid { - } SolidBox { translation 0 0 2.2799999999999985 } diff --git a/tests/parser/worlds/proto_not_allowed_mf_field_value.wbt b/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt similarity index 86% rename from tests/parser/worlds/proto_not_allowed_mf_field_value.wbt rename to tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt index 89521758b23..db9f54a28dc 100644 --- a/tests/parser/worlds/proto_not_allowed_mf_field_value.wbt +++ b/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt @@ -1,5 +1,6 @@ #VRML_SIM R2024a utf8 +EXTERNPROTO "webots://tests/manual_tests/protos/DerivedAppearanceProto.proto" EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" @@ -29,7 +30,7 @@ ProtoRestrictedFieldValues { rotation 0 1 0 1.5708 translation 0 0 0 extensionSlot [ - Group { + DerivedAppearanceProto { } Transform { } diff --git a/tests/parser/worlds/proto_not_allowed_mf_field_value_proto.wbt b/tests/parser/worlds/proto_not_allowed_mf_field_value_proto.wbt new file mode 100644 index 00000000000..1c5daf410d6 --- /dev/null +++ b/tests/parser/worlds/proto_not_allowed_mf_field_value_proto.wbt @@ -0,0 +1,41 @@ +#VRML_SIM R2024a utf8 + +EXTERNPROTO "webots://tests/manual_tests/protos/DerivedAppearanceProto.proto" +EXTERNPROTO "webots://tests/manual_tests/protos/Parameter.proto" +EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" +EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" +EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" + +WorldInfo { +} +Viewpoint { + orientation 0.7470290283624516 0.646616024550071 0.15438700586161153 5.42889 + position -1.2077 1.6424 1.96945 +} +Background { + skyColor [ + 0.4 0.7 1 + ] +} +ParserTestSupervisor { +} +DirectionalLight { + ambientIntensity 1 + direction -0.33 -1 -0.5 + castShadows TRUE +} +Floor { + rotation 1 0 0 -1.5708 +} +ProtoRestrictedFieldValues { + rotation 0 1 0 1.5708 + translation 0 0 0 + extensionSlot [ + DerivedAppearanceProto { + } + Parameter { + } + Solid { + } + ] +} From c8efce6796c6aebd725d8b14b2ba3b165bdb50c8 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Wed, 3 Jul 2024 01:31:17 -0700 Subject: [PATCH 05/37] run clang-format Signed-off-by: CoolSpy3 --- src/webots/vrml/WbFieldModel.cpp | 7 +++++-- src/webots/vrml/WbNodeModel.cpp | 5 ++++- src/webots/vrml/WbNodeModel.hpp | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/webots/vrml/WbFieldModel.cpp b/src/webots/vrml/WbFieldModel.cpp index 2fde876210f..3e619dc5a20 100644 --- a/src/webots/vrml/WbFieldModel.cpp +++ b/src/webots/vrml/WbFieldModel.cpp @@ -212,7 +212,9 @@ bool WbFieldModel::isValueAccepted(const WbValue *value, int *refusedIndex) cons assert(mfNode); foreach (const WbVariant acceptedVariant, mAcceptedValues) { const WbNode *nodeAccepted = acceptedVariant.toNode(); - if (nodeAccepted && (nodeAccepted->isProtoInstance() ? isProtoNodeTypeAccepted(nodeAccepted->modelName(), mfNode->item(i)->proto()) : isBaseNodeTypeAccepted(nodeAccepted->nodeModelName(), mfNode->item(i)->model()))) { + if (nodeAccepted && (nodeAccepted->isProtoInstance() ? + isProtoNodeTypeAccepted(nodeAccepted->modelName(), mfNode->item(i)->proto()) : + isBaseNodeTypeAccepted(nodeAccepted->nodeModelName(), mfNode->item(i)->model()))) { accepted = true; break; } @@ -241,7 +243,8 @@ bool WbFieldModel::isValueAccepted(const WbValue *value, int *refusedIndex) cons return true; const WbNode *nodeAccepted = acceptedVariant.toNode(); assert(nodeAccepted); - if (nodeAccepted->isProtoInstance() ? isProtoNodeTypeAccepted(nodeAccepted->modelName(), sfNode->value()->proto()) : isBaseNodeTypeAccepted(nodeAccepted->nodeModelName(), sfNode->value()->model())) + if (nodeAccepted->isProtoInstance() ? isProtoNodeTypeAccepted(nodeAccepted->modelName(), sfNode->value()->proto()) : + isBaseNodeTypeAccepted(nodeAccepted->nodeModelName(), sfNode->value()->model())) return true; } else if (singleValue->variantValue() == acceptedVariant) return true; diff --git a/src/webots/vrml/WbNodeModel.cpp b/src/webots/vrml/WbNodeModel.cpp index ae51f2da52c..f4cc6d46f02 100644 --- a/src/webots/vrml/WbNodeModel.cpp +++ b/src/webots/vrml/WbNodeModel.cpp @@ -34,7 +34,10 @@ void WbNodeModel::cleanup() { delete it.next().value(); } -WbNodeModel::WbNodeModel(WbTokenizer *tokenizer) : mInfo(tokenizer->info()), mName(tokenizer->nextWord()), mParentName(tokenizer->parent()) { +WbNodeModel::WbNodeModel(WbTokenizer *tokenizer) : + mInfo(tokenizer->info()), + mName(tokenizer->nextWord()), + mParentName(tokenizer->parent()) { tokenizer->skipToken("{"); while (tokenizer->peekWord() != "}") { diff --git a/src/webots/vrml/WbNodeModel.hpp b/src/webots/vrml/WbNodeModel.hpp index 625910cef90..10c9b582093 100644 --- a/src/webots/vrml/WbNodeModel.hpp +++ b/src/webots/vrml/WbNodeModel.hpp @@ -51,7 +51,8 @@ class WbNodeModel { const QList &fieldModels() const { return mFieldModels; } QStringList fieldNames(); - // parent nodes (only defined for base nodes. For proto nodes, see WbProtoModel::ancestorProtoName/ancestorProtoNameModel/baseType) + // parent nodes (only defined for base nodes. For proto nodes, see + // WbProtoModel::ancestorProtoName/ancestorProtoNameModel/baseType) const QString parentName() const { return mParentName; }; const WbNodeModel *parentModel() const { return mParentModel; }; From ae41015ef2ef3dd11d9427f5632a6896a2e470c2 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 02:06:46 -0700 Subject: [PATCH 06/37] workaround Transform edge case --- tests/parser/expected_results.txt | 2 +- tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/parser/expected_results.txt b/tests/parser/expected_results.txt index 382a4988612..9817ceabd84 100644 --- a/tests/parser/expected_results.txt +++ b/tests/parser/expected_results.txt @@ -25,7 +25,7 @@ parser/worlds/proto_missing_ending_curly_bracket.wbt "Expected field name or '}' parser/worlds/proto_missing_item_value_in_field.wbt "Expected floating point value, found 'field'." parser/worlds/proto_nested_parameter.wbt VOID parser/worlds/proto_not_allowed_field_value.wbt "Invalid 'translation' changed to 0 0 0. The value should be in the list: {0 0 0}." -parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Transform' removed from 'extensionSlot' field. The values should be in the list: {Solid, AppearanceProto}." +parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Pose' removed from 'extensionSlot' field. The values should be in the list: {Solid, AppearanceProto}." parser/worlds/proto_not_allowed_mf_field_value_proto.wbt "Invalid 'Parameter' removed from 'extensionSlot' field. The values should be in the list: {Solid, AppearanceProto}." parser/worlds/proto_parameter_missing_ending_curly_bracket.wbt "Expected field name or '}', found ']'." parser/worlds/proto_typo_in_alias.wbt "PROTO parameter 'rotation' has no matching IS field." diff --git a/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt b/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt index db9f54a28dc..692655a164d 100644 --- a/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt +++ b/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt @@ -32,7 +32,7 @@ ProtoRestrictedFieldValues { extensionSlot [ DerivedAppearanceProto { } - Transform { + Pose { } Solid { } From 517e0dd69fba8ea9d8cb4053f6cbfcb08c17f26a Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 02:11:31 -0700 Subject: [PATCH 07/37] fix spacing in documentation --- docs/reference/proto-definition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/proto-definition.md b/docs/reference/proto-definition.md index 2a60e85e0cd..e0d34add141 100644 --- a/docs/reference/proto-definition.md +++ b/docs/reference/proto-definition.md @@ -77,7 +77,7 @@ PROTO MyProto [ field SFString name "my proto" field SFColor{0 0 0, 0.5 0.5 0.5, 1 1 1} color 0.5 0.5 0.5 field SFNode physics NULL - field MFNode{Solid{}, Transform{}} extensionSlot [] + field MFNode{Solid{}, Transform{}} extensionSlot [] ] ``` From 72c7e6af3ca57a2b1f4876c7cd682f03f2ba193f Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 02:43:04 -0700 Subject: [PATCH 08/37] update changelog --- docs/reference/changelog-r2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/changelog-r2024.md b/docs/reference/changelog-r2024.md index 7fe1315b60d..443dba23f11 100644 --- a/docs/reference/changelog-r2024.md +++ b/docs/reference/changelog-r2024.md @@ -6,10 +6,10 @@ Released on December **th, 2023. - **Change the name of the web scene format from `X3D` to `W3D` ([#6280](https://github.com/cyberbotics/webots/pull/6280)).** - Enhancements - Improved the image range of the rotating [Lidar](lidar.md) ([#6324](https://github.com/cyberbotics/webots/pull/6324)). + - Updated proto node restrictions to consider node ancestry ([#6574](https://github.com/cyberbotics/webots/pull/6574)). - Cleanup - Removed deprecated `windowPosition`, `pixelSize` fields of [Display](display.md) node ([#6327](https://github.com/cyberbotics/webots/pull/6327)). - Bug Fixes - Fixed error message on Windows when `libssl-3-x64.dll` was added to `PATH` ([#6553](https://github.com/cyberbotics/webots/pull/6553)). - Fixed length of arrays returned by `getPose()` in Java ([#6556](https://github.com/cyberbotics/webots/pull/6556)). - Fixed length of arrays returned by `CameraRecognitionObject.getColors()` in Java ([#6564](https://github.com/cyberbotics/webots/pull/6564)) - From 1b8bb81f9b683ca89764e278122df7145236a501 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 02:55:16 -0700 Subject: [PATCH 09/37] initialize mParentModel in constructor --- src/webots/vrml/WbNodeModel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webots/vrml/WbNodeModel.cpp b/src/webots/vrml/WbNodeModel.cpp index f4cc6d46f02..9948fb1eb94 100644 --- a/src/webots/vrml/WbNodeModel.cpp +++ b/src/webots/vrml/WbNodeModel.cpp @@ -37,7 +37,8 @@ void WbNodeModel::cleanup() { WbNodeModel::WbNodeModel(WbTokenizer *tokenizer) : mInfo(tokenizer->info()), mName(tokenizer->nextWord()), - mParentName(tokenizer->parent()) { + mParentName(tokenizer->parent()), + mParentModel(NULL) { tokenizer->skipToken("{"); while (tokenizer->peekWord() != "}") { From 3ff040d8f7c84681d01d02cb76c93ed0cda9fdb7 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 09:45:00 -0700 Subject: [PATCH 10/37] fix typo --- src/webots/vrml/WbNodeModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webots/vrml/WbNodeModel.cpp b/src/webots/vrml/WbNodeModel.cpp index 9948fb1eb94..e55bef727a0 100644 --- a/src/webots/vrml/WbNodeModel.cpp +++ b/src/webots/vrml/WbNodeModel.cpp @@ -82,7 +82,7 @@ void WbNodeModel::readAllModels() { cModels.insert(model->name(), model); } - // Now that all the models are loaded, populate the ancestory tree + // Now that all the models are loaded, populate the ancestry tree foreach (QString baseModelName, baseModelNames()) { WbNodeModel *baseModel = findModel(baseModelName); if (baseModel) From 42477d189577e84a3bb5417dc138ebaaf94f57e3 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 11:20:30 -0700 Subject: [PATCH 11/37] fix DerivedAppearanceProto file extension --- .../{DerivedApperanceProto.prot => DerivedApperanceProto.proto} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/manual_tests/protos/{DerivedApperanceProto.prot => DerivedApperanceProto.proto} (100%) diff --git a/tests/manual_tests/protos/DerivedApperanceProto.prot b/tests/manual_tests/protos/DerivedApperanceProto.proto similarity index 100% rename from tests/manual_tests/protos/DerivedApperanceProto.prot rename to tests/manual_tests/protos/DerivedApperanceProto.proto From 806ebe7a1c604af3777e46e27e0cd2c25091a317 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 11:24:35 -0700 Subject: [PATCH 12/37] add proto_allowed_mf_field_value test --- tests/parser/expected_results.txt | 1 + .../worlds/proto_allowed_mf_field_value.wbt | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/parser/worlds/proto_allowed_mf_field_value.wbt diff --git a/tests/parser/expected_results.txt b/tests/parser/expected_results.txt index 9817ceabd84..09578d2f135 100644 --- a/tests/parser/expected_results.txt +++ b/tests/parser/expected_results.txt @@ -17,6 +17,7 @@ parser/worlds/missing_use_value.wbt "Expected field name or '}', found 'USE'." parser/worlds/nested_def_name.wbt VOID parser/worlds/nested_proto_missing_ending_curly_bracket.wbt "Expected parameter name or '}', found end of file." parser/worlds/node_without_brackets.wbt "Expected '{', found 'RectangleArena'." +parser/worlds/proto_allowed_mf_field_value.wbt VOID parser/worlds/proto_concatenated_comment.wbt VOID parser/worlds/proto_default_def_parameter.wbt "Wrong order of fields in the instance of PROTO DefaultDef: USE nodes might refer to the wrong DEF nodes." parser/worlds/proto_mismatch_field_type.wbt "Type mismatch between 'fieldTexture' PROTO parameter and field 'url'." diff --git a/tests/parser/worlds/proto_allowed_mf_field_value.wbt b/tests/parser/worlds/proto_allowed_mf_field_value.wbt new file mode 100644 index 00000000000..401f8cd69b9 --- /dev/null +++ b/tests/parser/worlds/proto_allowed_mf_field_value.wbt @@ -0,0 +1,38 @@ +#VRML_SIM R2024a utf8 + +EXTERNPROTO "webots://tests/manual_tests/protos/DerivedAppearanceProto.proto" +EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" +EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" +EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" + +WorldInfo { +} +Viewpoint { + orientation 0.7470290283624516 0.646616024550071 0.15438700586161153 5.42889 + position -1.2077 1.6424 1.96945 +} +Background { + skyColor [ + 0.4 0.7 1 + ] +} +ParserTestSupervisor { +} +DirectionalLight { + ambientIntensity 1 + direction -0.33 -1 -0.5 + castShadows TRUE +} +Floor { + rotation 1 0 0 -1.5708 +} +ProtoRestrictedFieldValues { + rotation 0 1 0 1.5708 + translation 0 0 0 + extensionSlot [ + DerivedAppearanceProto { + } + Solid { + } + ] +} From 116a96b7f5dda94a3538b852b9b48126907b78e5 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 12:01:02 -0700 Subject: [PATCH 13/37] fix DerivedAppearanceProto filename --- .../{DerivedApperanceProto.proto => DerivedAppearanceProto.proto} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/manual_tests/protos/{DerivedApperanceProto.proto => DerivedAppearanceProto.proto} (100%) diff --git a/tests/manual_tests/protos/DerivedApperanceProto.proto b/tests/manual_tests/protos/DerivedAppearanceProto.proto similarity index 100% rename from tests/manual_tests/protos/DerivedApperanceProto.proto rename to tests/manual_tests/protos/DerivedAppearanceProto.proto From 2fb9daca25665d082b1b6bcebf30084421116552 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Wed, 3 Jul 2024 12:38:16 -0700 Subject: [PATCH 14/37] fix test suite --- .../protos/DerivedAppearanceProto.proto | 12 ------------ tests/manual_tests/protos/DerivedPoseProto.proto | 14 ++++++++++++++ tests/manual_tests/protos/PoseProto.proto | 12 ++++++++++++ .../parser/protos/ProtoRestrictedFieldValues.proto | 4 ++-- .../parser/worlds/proto_allowed_mf_field_value.wbt | 6 ++++-- .../proto_not_allowed_mf_field_value_base.wbt | 4 ++-- .../proto_not_allowed_mf_field_value_proto.wbt | 4 ++-- 7 files changed, 36 insertions(+), 20 deletions(-) delete mode 100644 tests/manual_tests/protos/DerivedAppearanceProto.proto create mode 100644 tests/manual_tests/protos/DerivedPoseProto.proto create mode 100644 tests/manual_tests/protos/PoseProto.proto diff --git a/tests/manual_tests/protos/DerivedAppearanceProto.proto b/tests/manual_tests/protos/DerivedAppearanceProto.proto deleted file mode 100644 index 2230d5a9cf1..00000000000 --- a/tests/manual_tests/protos/DerivedAppearanceProto.proto +++ /dev/null @@ -1,12 +0,0 @@ -#VRML_SIM R2024a utf8 - -EXTERNPROTO "webots://tests/manual_tests/protos/AppearanceProto.proto" - -PROTO DerivedAppearanceProto [ - field SFColor color 0 0 0 -] -{ - AppearanceProto { - color IS color - } -} diff --git a/tests/manual_tests/protos/DerivedPoseProto.proto b/tests/manual_tests/protos/DerivedPoseProto.proto new file mode 100644 index 00000000000..c74bb7f1c48 --- /dev/null +++ b/tests/manual_tests/protos/DerivedPoseProto.proto @@ -0,0 +1,14 @@ +#VRML_SIM R2024a utf8 + +EXTERNPROTO "webots://tests/manual_tests/protos/PoseProto.proto" + +PROTO Pose [ + field SFVec3f translation 0 0 0 + field SFVec3f rotation 0 0 1 0 +] +{ + PoseProto { + translation IS translation + rotation IS rotation + } +} diff --git a/tests/manual_tests/protos/PoseProto.proto b/tests/manual_tests/protos/PoseProto.proto new file mode 100644 index 00000000000..8aefe6b2eda --- /dev/null +++ b/tests/manual_tests/protos/PoseProto.proto @@ -0,0 +1,12 @@ +#VRML_SIM R2024a utf8 + +PROTO Pose [ + field SFVec3f translation 0 0 0 + field SFVec3f rotation 0 0 1 0 +] +{ + Pose { + translation IS translation + rotation IS rotation + } +} diff --git a/tests/parser/protos/ProtoRestrictedFieldValues.proto b/tests/parser/protos/ProtoRestrictedFieldValues.proto index 90745919593..ca6c0546fe9 100644 --- a/tests/parser/protos/ProtoRestrictedFieldValues.proto +++ b/tests/parser/protos/ProtoRestrictedFieldValues.proto @@ -1,12 +1,12 @@ #VRML_SIM R2024a utf8 -EXTERNPROTO "webots://tests/manual_tests/protos/AppearanceProto.proto" +EXTERNPROTO "webots://tests/manual_tests/protos/PoseProto.proto" PROTO ProtoRestrictedFieldValues [ field SFRotation{0 1 0 0, 0 0 1 0, 0 1 0 1.5708} rotation 0 0 1 0 field SFVec3f{0 0 0} translation 0 0 0 - field MFNode{Solid{}, AppearanceProto{}} extensionSlot [] + field MFNode{Solid{}, PoseProto{}} extensionSlot [] ] { Solid { diff --git a/tests/parser/worlds/proto_allowed_mf_field_value.wbt b/tests/parser/worlds/proto_allowed_mf_field_value.wbt index 401f8cd69b9..658d6154566 100644 --- a/tests/parser/worlds/proto_allowed_mf_field_value.wbt +++ b/tests/parser/worlds/proto_allowed_mf_field_value.wbt @@ -1,6 +1,6 @@ #VRML_SIM R2024a utf8 -EXTERNPROTO "webots://tests/manual_tests/protos/DerivedAppearanceProto.proto" +EXTERNPROTO "webots://tests/manual_tests/protos/DerivedPoseProto.proto" EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" @@ -30,9 +30,11 @@ ProtoRestrictedFieldValues { rotation 0 1 0 1.5708 translation 0 0 0 extensionSlot [ - DerivedAppearanceProto { + DerivedPoseProto { } Solid { } + Accelerometer { + } ] } diff --git a/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt b/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt index 692655a164d..b10621b1ecb 100644 --- a/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt +++ b/tests/parser/worlds/proto_not_allowed_mf_field_value_base.wbt @@ -1,6 +1,6 @@ #VRML_SIM R2024a utf8 -EXTERNPROTO "webots://tests/manual_tests/protos/DerivedAppearanceProto.proto" +EXTERNPROTO "webots://tests/manual_tests/protos/DerivedPoseProto.proto" EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" @@ -30,7 +30,7 @@ ProtoRestrictedFieldValues { rotation 0 1 0 1.5708 translation 0 0 0 extensionSlot [ - DerivedAppearanceProto { + DerivedPoseProto { } Pose { } diff --git a/tests/parser/worlds/proto_not_allowed_mf_field_value_proto.wbt b/tests/parser/worlds/proto_not_allowed_mf_field_value_proto.wbt index 1c5daf410d6..d0c3df1b4c2 100644 --- a/tests/parser/worlds/proto_not_allowed_mf_field_value_proto.wbt +++ b/tests/parser/worlds/proto_not_allowed_mf_field_value_proto.wbt @@ -1,6 +1,6 @@ #VRML_SIM R2024a utf8 -EXTERNPROTO "webots://tests/manual_tests/protos/DerivedAppearanceProto.proto" +EXTERNPROTO "webots://tests/manual_tests/protos/DerivedPoseProto.proto" EXTERNPROTO "webots://tests/manual_tests/protos/Parameter.proto" EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" @@ -31,7 +31,7 @@ ProtoRestrictedFieldValues { rotation 0 1 0 1.5708 translation 0 0 0 extensionSlot [ - DerivedAppearanceProto { + DerivedPoseProto { } Parameter { } From ee1c7327c14e9f2802bcec49684ae5363f0e8c37 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:23:21 -0700 Subject: [PATCH 15/37] fix test suite --- tests/manual_tests/protos/DerivedPoseProto.proto | 4 ++-- tests/manual_tests/protos/PoseProto.proto | 4 ++-- tests/parser/expected_results.txt | 4 ++-- tests/parser/worlds/proto_allowed_mf_field_value.wbt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/manual_tests/protos/DerivedPoseProto.proto b/tests/manual_tests/protos/DerivedPoseProto.proto index c74bb7f1c48..6e71e943871 100644 --- a/tests/manual_tests/protos/DerivedPoseProto.proto +++ b/tests/manual_tests/protos/DerivedPoseProto.proto @@ -2,9 +2,9 @@ EXTERNPROTO "webots://tests/manual_tests/protos/PoseProto.proto" -PROTO Pose [ +PROTO DerivedPoseProto [ field SFVec3f translation 0 0 0 - field SFVec3f rotation 0 0 1 0 + field SFRotation rotation 0 0 1 0 ] { PoseProto { diff --git a/tests/manual_tests/protos/PoseProto.proto b/tests/manual_tests/protos/PoseProto.proto index 8aefe6b2eda..3fff815c07b 100644 --- a/tests/manual_tests/protos/PoseProto.proto +++ b/tests/manual_tests/protos/PoseProto.proto @@ -1,8 +1,8 @@ #VRML_SIM R2024a utf8 -PROTO Pose [ +PROTO PoseProto [ field SFVec3f translation 0 0 0 - field SFVec3f rotation 0 0 1 0 + field SFRotation rotation 0 0 1 0 ] { Pose { diff --git a/tests/parser/expected_results.txt b/tests/parser/expected_results.txt index 09578d2f135..3f1cf618856 100644 --- a/tests/parser/expected_results.txt +++ b/tests/parser/expected_results.txt @@ -26,8 +26,8 @@ parser/worlds/proto_missing_ending_curly_bracket.wbt "Expected field name or '}' parser/worlds/proto_missing_item_value_in_field.wbt "Expected floating point value, found 'field'." parser/worlds/proto_nested_parameter.wbt VOID parser/worlds/proto_not_allowed_field_value.wbt "Invalid 'translation' changed to 0 0 0. The value should be in the list: {0 0 0}." -parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Pose' removed from 'extensionSlot' field. The values should be in the list: {Solid, AppearanceProto}." -parser/worlds/proto_not_allowed_mf_field_value_proto.wbt "Invalid 'Parameter' removed from 'extensionSlot' field. The values should be in the list: {Solid, AppearanceProto}." +parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Pose' removed from 'extensionSlot' field. The values should be in the list: {Solid, PoseProto}." +parser/worlds/proto_not_allowed_mf_field_value_proto.wbt "Invalid 'Parameter' removed from 'extensionSlot' field. The values should be in the list: {Solid, PoseProto}." parser/worlds/proto_parameter_missing_ending_curly_bracket.wbt "Expected field name or '}', found ']'." parser/worlds/proto_typo_in_alias.wbt "PROTO parameter 'rotation' has no matching IS field." parser/worlds/proto_typo_in_is_keyword.wbt "Expected floating point value, found 'ISNOT'." diff --git a/tests/parser/worlds/proto_allowed_mf_field_value.wbt b/tests/parser/worlds/proto_allowed_mf_field_value.wbt index 658d6154566..7b23391de7b 100644 --- a/tests/parser/worlds/proto_allowed_mf_field_value.wbt +++ b/tests/parser/worlds/proto_allowed_mf_field_value.wbt @@ -34,7 +34,7 @@ ProtoRestrictedFieldValues { } Solid { } - Accelerometer { + Robot { } ] } From 11f33ad7208a2ec8673e41edceb74d2e77409993 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sat, 6 Jul 2024 00:45:25 -0700 Subject: [PATCH 16/37] create WbFieldValueRestriction --- src/webots/Makefile | 1 + src/webots/vrml/WbFieldValueRestriction.cpp | 62 +++++++++++++++++++++ src/webots/vrml/WbFieldValueRestriction.hpp | 58 +++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 src/webots/vrml/WbFieldValueRestriction.cpp create mode 100644 src/webots/vrml/WbFieldValueRestriction.hpp diff --git a/src/webots/Makefile b/src/webots/Makefile index 59a153b33dd..45ec5f6c390 100644 --- a/src/webots/Makefile +++ b/src/webots/Makefile @@ -290,6 +290,7 @@ QT_SOURCES = WbAboutBox.cpp \ WbFieldIntSpinBox.cpp \ WbFieldLineEdit.cpp \ WbFindReplaceDialog.cpp \ + WbFieldValueRestriction.cpp \ WbFluid.cpp \ WbFocus.cpp \ WbFog.cpp \ diff --git a/src/webots/vrml/WbFieldValueRestriction.cpp b/src/webots/vrml/WbFieldValueRestriction.cpp new file mode 100644 index 00000000000..83bf0732c9b --- /dev/null +++ b/src/webots/vrml/WbFieldValueRestriction.cpp @@ -0,0 +1,62 @@ +// Copyright 1996-2023 Cyberbotics Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "WbFieldValueRestriction.hpp" + +#include "WbNode.hpp" + +WbFieldValueRestriction &WbFieldValueRestriction::operator=(const WbFieldValueRestriction &other) { + WbVariant::operator =( other ); + mAllowsSubtypes = other.allowsSubtypes(); + return *this; +} + +bool WbFieldValueRestriction::operator==(const WbFieldValueRestriction &other) const { + return WbVariant::operator ==( other ) && allowsSubtypes() == other.allowsSubtypes(); +} + +bool WbFieldValueRestriction::operator!=(const WbFieldValueRestriction &other) const { + return !(*this == other); +} + +bool WbFieldValueRestriction::isVariantAccepted(const WbVariant &variant) const { + if (type() != variant.type()) + return false; + if (type() != WB_SF_NODE) + return variant == *this; + return isNodeAccepted(variant.toNode()); +} + +bool WbFieldValueRestriction::isNodeAccepted(const WbNode *node) const { + if(type() != WB_SF_NODE) + return false; + if(!toNode() || !node) + return toNode() == node; + if (allowsSubtypes()) + return toNode()->isProtoInstance() ? isProtoNodeTypeAccepted(node->proto()) : + isBaseNodeTypeAccepted(node->model()); + return toNode()->modelName() == node->modelName() || toNode()->modelName() == node->nodeModelName(); +} + +bool WbFieldValueRestriction::isBaseNodeTypeAccepted(const WbNodeModel *actualType) const { + if (type() != WB_SF_NODE || !actualType) + return false; + return toNode()->modelName() == actualType->name() || isBaseNodeTypeAccepted(actualType->parentModel()); +} + +bool WbFieldValueRestriction::isProtoNodeTypeAccepted(const WbProtoModel *actualType) const { + if (type() != WB_SF_NODE || !actualType) + return false; + return toNode()->modelName() == actualType->name() || isProtoNodeTypeAccepted(actualType->ancestorProtoModel()); +} diff --git a/src/webots/vrml/WbFieldValueRestriction.hpp b/src/webots/vrml/WbFieldValueRestriction.hpp new file mode 100644 index 00000000000..8b5e636c4b1 --- /dev/null +++ b/src/webots/vrml/WbFieldValueRestriction.hpp @@ -0,0 +1,58 @@ +// Copyright 1996-2023 Cyberbotics Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef WB_FIELD_VALUE_RESTRICTION +#define WB_FIELD_VALUE_RESTRICTION + +#include +#include +#include +#include + +#include "../../../include/controller/c/webots/supervisor.h" + +class WbFieldValueRestriction: public WbVariant { + Q_OBJECT; + +public: + WbFieldValueRestriction() : WbVariant(), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(bool b) : WbVariant(b), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(int i) : WbVariant(i), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(double d) : WbVariant(d), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(const QString &s) : WbVariant(s), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(const WbVector2 &v) : WbVariant(v), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(const WbVector3 &v) : WbVariant(v), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(const WbRgb &c) : WbVariant(c), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(const WbRotation &r) : WbVariant(r), mAllowsSubtypes(false) {} + explicit WbFieldValueRestriction(WbNode *n, bool allowsSubtypes) : WbVariant(n), mAllowsSubtypes(allowsSubtypes) {} + explicit WbFieldValueRestriction(const WbVariant &variant, bool allowsSubtypes) : WbVariant(variant), mAllowsSubtypes(allowsSubtypes && variant.type() == WB_SF_NODE) {} + WbFieldValueRestriction &operator=(const WbFieldValueRestriction &other); + bool operator==(const WbFieldValueRestriction &other) const; + bool operator!=(const WbFieldValueRestriction &other) const; + + virtual ~WbFieldValueRestriction() {} + + const bool allowsSubtypes() const { return mAllowsSubtypes; } + + bool isVariantAccepted(const WbVariant &node) const; + bool isNodeAccepted(const WbNode *node) const; + bool isBaseNodeTypeAccepted(const WbNodeModel *actualType) const; + bool isProtoNodeTypeAccepted(const WbProtoModel *actualType) const; + +private: + // If the restriction is a node type, whether the node type can be a model which "extends" the requested type + bool mAllowsSubtypes; +}; + +#endif From 24bcb24524f0c0abda97d5be5557cabee7d50a32 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sat, 6 Jul 2024 00:47:08 -0700 Subject: [PATCH 17/37] add parsing of '=' syntax to reenable old behavior --- src/webots/vrml/WbFieldModel.cpp | 82 +++++++++++--------------------- src/webots/vrml/WbFieldModel.hpp | 9 ++-- src/webots/vrml/WbParser.cpp | 2 + 3 files changed, 35 insertions(+), 58 deletions(-) diff --git a/src/webots/vrml/WbFieldModel.cpp b/src/webots/vrml/WbFieldModel.cpp index 3e619dc5a20..1da050f7a1c 100644 --- a/src/webots/vrml/WbFieldModel.cpp +++ b/src/webots/vrml/WbFieldModel.cpp @@ -38,6 +38,7 @@ #include "WbToken.hpp" #include "WbTokenizer.hpp" #include "WbValue.hpp" +#include "WbVersion.hpp" #include "WbWriter.hpp" #include @@ -102,11 +103,11 @@ WbFieldModel::WbFieldModel(WbTokenizer *tokenizer, const QString &worldPath) { defaultValueIsValid = false; WbMultipleValue *multipleValue = dynamic_cast(mDefaultValue); if (multipleValue) - mAcceptedValues << multipleValue->variantValue(refusedIndex); + mAcceptedValues << WbFieldValueRestriction(multipleValue->variantValue(refusedIndex), false); else { WbSingleValue *singleValue = dynamic_cast(mDefaultValue); assert(singleValue); - mAcceptedValues << singleValue->variantValue(); + mAcceptedValues << WbFieldValueRestriction(singleValue->variantValue(), false); } } if (!defaultValueIsValid) @@ -176,23 +177,34 @@ WbValue *WbFieldModel::createValueForVrmlType(const QString &type, WbTokenizer * return NULL; } -QList WbFieldModel::getAcceptedValues(const QString &type, WbTokenizer *tokenizer, const QString &worldPath) { - QList values; +QList WbFieldModel::getAcceptedValues(const QString &type, WbTokenizer *tokenizer, const QString &worldPath) { + QList values; while (tokenizer->nextWord() != '}') { tokenizer->ungetToken(); + + bool allowSubtypeMatch; + if (tokenizer->fileVersion() >= WbVersion(2024, 0, 0)) { + if(tokenizer->peekWord() == '=') { + tokenizer->nextToken(); + allowSubtypeMatch = false; + } else + allowSubtypeMatch = true; + } else + allowSubtypeMatch = false; + const WbSingleValue *singleValue = dynamic_cast(WbFieldModel::createValueForVrmlType(type, tokenizer, worldPath)); assert(singleValue); - WbVariant variant(singleValue->variantValue()); - if (type == "SFNode" && variant.toNode()) { + WbFieldValueRestriction restriction(singleValue->variantValue(), allowSubtypeMatch); + if (type == "SFNode" && restriction.toNode()) { // explicit copy of the node to be persistent. - WbNode *copy = variant.toNode()->cloneAndReferenceProtoInstance(); - variant.setNode(copy, true); - QObject::connect(&variant, &QObject::destroyed, copy, &QObject::deleteLater); + WbNode *copy = restriction.toNode()->cloneAndReferenceProtoInstance(); + restriction.setNode(copy, true); + QObject::connect(&restriction, &QObject::destroyed, copy, &QObject::deleteLater); } - values << variant; + values << restriction; delete singleValue; } return values; @@ -207,24 +219,10 @@ bool WbFieldModel::isValueAccepted(const WbValue *value, int *refusedIndex) cons if (multipleValue) { for (int i = 0; i < multipleValue->size(); ++i) { bool accepted = false; - if (type() == WB_MF_NODE) { - const WbMFNode *mfNode = static_cast(value); - assert(mfNode); - foreach (const WbVariant acceptedVariant, mAcceptedValues) { - const WbNode *nodeAccepted = acceptedVariant.toNode(); - if (nodeAccepted && (nodeAccepted->isProtoInstance() ? - isProtoNodeTypeAccepted(nodeAccepted->modelName(), mfNode->item(i)->proto()) : - isBaseNodeTypeAccepted(nodeAccepted->nodeModelName(), mfNode->item(i)->model()))) { - accepted = true; - break; - } - } - } else { - foreach (const WbVariant acceptedVariant, mAcceptedValues) { - if (multipleValue->variantValue(i) == acceptedVariant) { - accepted = true; - break; - } + foreach (const WbFieldValueRestriction acceptedVariant, mAcceptedValues) { + if (acceptedVariant.isVariantAccepted(multipleValue->variantValue(i))) { + accepted = true; + break; } } if (!accepted) { @@ -235,35 +233,13 @@ bool WbFieldModel::isValueAccepted(const WbValue *value, int *refusedIndex) cons return true; } else { assert(singleValue); - foreach (const WbVariant acceptedVariant, mAcceptedValues) { - if (type() == WB_SF_NODE) { - const WbSFNode *sfNode = static_cast(value); - assert(sfNode); - if (!sfNode->value()) - return true; - const WbNode *nodeAccepted = acceptedVariant.toNode(); - assert(nodeAccepted); - if (nodeAccepted->isProtoInstance() ? isProtoNodeTypeAccepted(nodeAccepted->modelName(), sfNode->value()->proto()) : - isBaseNodeTypeAccepted(nodeAccepted->nodeModelName(), sfNode->value()->model())) - return true; - } else if (singleValue->variantValue() == acceptedVariant) + foreach (const WbFieldValueRestriction acceptedVariant, mAcceptedValues) { + if(acceptedVariant.isVariantAccepted(singleValue->variantValue())) return true; } *refusedIndex = 0; - } - return false; -} - -bool WbFieldModel::isBaseNodeTypeAccepted(const QString &expectedType, const WbNodeModel *actualType) const { - if (!actualType) return false; - return expectedType == actualType->name() || isBaseNodeTypeAccepted(expectedType, actualType->parentModel()); -} - -bool WbFieldModel::isProtoNodeTypeAccepted(const QString &expectedType, const WbProtoModel *actualType) const { - if (!actualType) - return false; - return expectedType == actualType->name() || isProtoNodeTypeAccepted(expectedType, actualType->ancestorProtoModel()); + } } bool WbFieldModel::isMultiple() const { diff --git a/src/webots/vrml/WbFieldModel.hpp b/src/webots/vrml/WbFieldModel.hpp index 91917c0b6e7..24b838ac746 100644 --- a/src/webots/vrml/WbFieldModel.hpp +++ b/src/webots/vrml/WbFieldModel.hpp @@ -21,6 +21,7 @@ // #include +#include #include #include #include @@ -55,10 +56,8 @@ class WbFieldModel { // accepted values bool isValueAccepted(const WbValue *value, int *refusedIndex) const; - bool isBaseNodeTypeAccepted(const QString &expectedType, const WbNodeModel *actualType) const; - bool isProtoNodeTypeAccepted(const QString &expectedType, const WbProtoModel *actualType) const; bool hasRestrictedValues() const { return !mAcceptedValues.isEmpty(); } - const QList acceptedValues() const { return mAcceptedValues; } + const QList acceptedValues() const { return mAcceptedValues; } // field type WbFieldType type() const { return mDefaultValue->type(); } @@ -93,13 +92,13 @@ class WbFieldModel { bool mIsDeprecated; bool mIsUnconnected; WbValue *mDefaultValue; - QList mAcceptedValues; // TODO: const WbVariant + QList mAcceptedValues; // TODO: const WbVariant WbToken *mNameToken; mutable int mRefCount; static WbValue *createValueForVrmlType(const QString &type, WbTokenizer *tokenizer, const QString &worldPath); - static QList getAcceptedValues(const QString &type, WbTokenizer *tokenizer, const QString &worldPath); + static QList getAcceptedValues(const QString &type, WbTokenizer *tokenizer, const QString &worldPath); }; #endif diff --git a/src/webots/vrml/WbParser.cpp b/src/webots/vrml/WbParser.cpp index 19b7ca7cab4..cdafa088f6c 100644 --- a/src/webots/vrml/WbParser.cpp +++ b/src/webots/vrml/WbParser.cpp @@ -150,6 +150,8 @@ void WbParser::parseFieldValue(WbFieldType type, const QString &worldPath) { void WbParser::parseFieldAcceptedValues(WbFieldType type, const QString &worldPath) { while (nextWord() != '}') { mTokenizer->ungetToken(); + if (nextWord() != '=') + mTokenizer->ungetToken(); WbParser::parseSingleFieldValue(type, worldPath); } } From c455435d4d234ca90bd780187c346105e9a27c4e Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sat, 6 Jul 2024 00:48:32 -0700 Subject: [PATCH 18/37] keep track of proto parents --- scripts/packaging/generate_proto_list.py | 3 +++ src/webots/vrml/WbProtoManager.cpp | 13 ++++++++++--- src/webots/vrml/WbProtoManager.hpp | 7 +++++-- src/webots/vrml/WbProtoModel.cpp | 9 +++++++++ src/webots/vrml/WbProtoModel.hpp | 2 ++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/scripts/packaging/generate_proto_list.py b/scripts/packaging/generate_proto_list.py index 2d5baece90e..cd6796cb99e 100644 --- a/scripts/packaging/generate_proto_list.py +++ b/scripts/packaging/generate_proto_list.py @@ -33,6 +33,7 @@ def __init__(self, path, name): self.path = path.replace('\\', '/') # use cross-platform forward slashes self.proto_type = None # direct node type, ex: for RoadSegment is Road self.base_type = None # lowest node type, ex: for RoadSegment is Solid + self.parents = [] # all proto types this type extends, ex: for Road Segment is [Road] self.license = None self.license_url = None self.description = '' @@ -154,6 +155,7 @@ def generate_proto_list(current_tag=None, silent=False): if info.base_type not in protos: # the current proto depends on a sub-proto: iterate until a base node is reached raise RuntimeError(f'Error: "{info.base_type}" proto node does not exist. Either it was skipped or the regex ' 'that retrieves the proto_type is incorrect.') + info.parents.append(info.base_type) sub_proto = protos[info.base_type] info.base_type = sub_proto.proto_type @@ -201,6 +203,7 @@ def generate_proto_list(current_tag=None, silent=False): proto_element = ET.SubElement(root, 'proto') ET.SubElement(proto_element, 'name').text = info.name ET.SubElement(proto_element, 'base-type').text = info.base_type + ET.SubElement(proto_element, 'parents').text = ','.join(info.parents) ET.SubElement(proto_element, 'url').text = info.path.replace(WEBOTS_HOME + '/', prefix) if info.license is not None: diff --git a/src/webots/vrml/WbProtoManager.cpp b/src/webots/vrml/WbProtoManager.cpp index 9d7676a50c5..5eaac4112e8 100644 --- a/src/webots/vrml/WbProtoManager.cpp +++ b/src/webots/vrml/WbProtoManager.cpp @@ -436,7 +436,7 @@ void WbProtoManager::loadWebotsProtoMap() { if (reader.name().toString() == "proto") { bool needsRobotAncestor = false; QString name, url, baseType, license, licenseUrl, documentationUrl, description, slotType; - QStringList tags, parameters; + QStringList tags, parameters, parents; while (reader.readNextStartElement()) { if (reader.name().toString() == "name") { name = reader.readElementText(); @@ -478,6 +478,10 @@ void WbProtoManager::loadWebotsProtoMap() { parameters = reader.readElementText().split("\\n", Qt::SkipEmptyParts); reader.readNext(); } + if (reader.name().toString() == "parents") { + parents = reader.readElementText().split(",", Qt::SkipEmptyParts); + reader.readNext(); + } if (reader.name().toString() == "needs-robot-ancestor") { needsRobotAncestor = reader.readElementText() == "true"; reader.readNext(); @@ -485,7 +489,7 @@ void WbProtoManager::loadWebotsProtoMap() { } description = description.replace("\\n", "\n"); WbProtoInfo *const info = new WbProtoInfo(url, baseType, license, licenseUrl, documentationUrl, description, slotType, - tags, parameters, needsRobotAncestor); + tags, parameters, parents, needsRobotAncestor); mWebotsProtoList.insert(name, info); } else reader.raiseError(tr("Expected 'proto' element.")); @@ -756,9 +760,12 @@ WbProtoInfo *WbProtoManager::generateInfoFromProtoFile(const QString &protoFileN parameters << field; } + // generate parents string (needed by PROTO wizard) + QStringList parents = protoModel->parentList(); + WbProtoInfo *info = new WbProtoInfo(url, protoModel->baseType(), protoModel->license(), protoModel->licenseUrl(), protoModel->documentationUrl(), protoModel->info(), protoModel->slotType(), - protoModel->tags(), parameters, needsRobotAncestor); + protoModel->tags(), parameters, parents, needsRobotAncestor); protoModel->destroy(); return info; diff --git a/src/webots/vrml/WbProtoManager.hpp b/src/webots/vrml/WbProtoManager.hpp index 6c2073d7437..50f0b89e260 100644 --- a/src/webots/vrml/WbProtoManager.hpp +++ b/src/webots/vrml/WbProtoManager.hpp @@ -56,7 +56,7 @@ class WbProtoInfo { public: WbProtoInfo(const QString &url, const QString &baseType, const QString &license, const QString &licenseUrl, const QString &documentationUrl, const QString &description, const QString &slotType, const QStringList &tags, - const QStringList ¶meters, bool needsRobotAncestor) : + const QStringList ¶meters, const QStringList &parents, bool needsRobotAncestor) : mUrl(url), mBaseType(baseType), mLicense(license), @@ -66,6 +66,7 @@ class WbProtoInfo { mSlotType(slotType), mTags(tags), mParameters(parameters), + mParents(parents), mNeedsRobotAncestor(needsRobotAncestor), mIsDirty(false) { // extract parameter names @@ -81,7 +82,7 @@ class WbProtoInfo { // copy constructor WbProtoInfo(const WbProtoInfo &other) : WbProtoInfo(other.mUrl, other.mBaseType, other.mLicense, other.mLicenseUrl, other.mDocumentationUrl, other.mDescription, - other.mSlotType, other.mTags, other.mParameters, other.mNeedsRobotAncestor) {} + other.mSlotType, other.mTags, other.mParameters, other.mParents, other.mNeedsRobotAncestor) {} const QString &url() const { return mUrl; } const QString &baseType() const { return mBaseType; } @@ -93,6 +94,7 @@ class WbProtoInfo { const QStringList &tags() const { return mTags; } const QStringList ¶meters() const { return mParameters; } const QStringList ¶meterNames() const { return mParameterNames; } + const QStringList &parents() const { return mParents; } const bool needsRobotAncestor() const { return mNeedsRobotAncestor; } void setDirty(bool value) { mIsDirty = value; } bool isDirty() const { return mIsDirty; } @@ -107,6 +109,7 @@ class WbProtoInfo { QString mSlotType; QStringList mTags; QStringList mParameters; + QStringList mParents; bool mNeedsRobotAncestor; bool mIsDirty; diff --git a/src/webots/vrml/WbProtoModel.cpp b/src/webots/vrml/WbProtoModel.cpp index b78fa5e7834..4494e107d82 100644 --- a/src/webots/vrml/WbProtoModel.cpp +++ b/src/webots/vrml/WbProtoModel.cpp @@ -417,6 +417,15 @@ WbNode *WbProtoModel::generateRoot(const QVector ¶meters, const Q return root; } +QStringList WbProtoModel::parentList() const { + QStringList parents; + const WbProtoModel *parentProtoModel = this; + while ((parentProtoModel = parentProtoModel->ancestorProtoModel())) { + parents << parentProtoModel->name(); + } + return parents; +} + void WbProtoModel::ref(bool isFromProtoInstanceCreation) { mRefCount++; if (isFromProtoInstanceCreation) diff --git a/src/webots/vrml/WbProtoModel.hpp b/src/webots/vrml/WbProtoModel.hpp index dccf030bd97..232a000293c 100644 --- a/src/webots/vrml/WbProtoModel.hpp +++ b/src/webots/vrml/WbProtoModel.hpp @@ -97,6 +97,8 @@ class WbProtoModel : public QObject { const QString &slotType() const { return mSlotType; } + QStringList parentList() const; + QStringList parameterNames() const; void setIsTemplate(bool value); From 1955c3b294cde09352a0c7220022d5da25f0af9b Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sat, 6 Jul 2024 00:49:47 -0700 Subject: [PATCH 19/37] update field validation checks --- src/webots/nodes/utils/WbDictionary.cpp | 12 ++++++----- src/webots/nodes/utils/WbNodeOperations.cpp | 4 ++-- src/webots/nodes/utils/WbNodeUtilities.cpp | 24 +++++++++++---------- src/webots/nodes/utils/WbNodeUtilities.hpp | 3 ++- src/webots/nodes/utils/WbWorld.cpp | 2 +- src/webots/scene_tree/WbAddNodeDialog.cpp | 15 ++++++------- src/webots/scene_tree/WbAddNodeDialog.hpp | 2 +- src/webots/scene_tree/WbSceneTree.cpp | 2 +- src/webots/scene_tree/WbValueEditor.cpp | 4 ++-- src/webots/user_commands/WbClipboard.cpp | 2 ++ src/webots/user_commands/WbClipboard.hpp | 1 + src/webots/vrml/WbField.cpp | 2 +- src/webots/vrml/WbField.hpp | 2 +- 13 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/webots/nodes/utils/WbDictionary.cpp b/src/webots/nodes/utils/WbDictionary.cpp index e10f8172589..41950efb643 100644 --- a/src/webots/nodes/utils/WbDictionary.cpp +++ b/src/webots/nodes/utils/WbDictionary.cpp @@ -121,7 +121,8 @@ bool WbDictionary::updateDef(WbBaseNode *&node, WbSFNode *sfNode, WbMFNode *mfNo QString error; assert(node->parentField() && node->parentNode()); typeMatch = WbNodeUtilities::isAllowedToInsert(node->parentField(), definitionNode->nodeModelName(), node->parentNode(), - error, nodeUse, QString(), QStringList(definitionNode->nodeModelName())); + error, nodeUse, QString(), definitionNode->modelName(), definitionNode->model(), + definitionNode->proto()); match = typeMatch && !definitionNode->isAnAncestorOf(node); } @@ -139,7 +140,8 @@ bool WbDictionary::updateDef(WbBaseNode *&node, WbSFNode *sfNode, WbMFNode *mfNo assert(node->parentField() && node->parentNode()); typeMatch = WbNodeUtilities::isAllowedToInsert(node->parentField(), definitionNode->nodeModelName(), node->parentNode(), error, - nodeUse, QString(), QStringList(definitionNode->nodeModelName())); + nodeUse, QString(), definitionNode->modelName(), definitionNode->model(), + definitionNode->proto()); } } @@ -465,7 +467,7 @@ bool WbDictionary::checkBoundingObjectConstraints(const WbBaseNode *defNode, QSt const WbNode *const n = sfnode->value(); if (n) { if (!WbNodeUtilities::isAllowedToInsert(fields[i], n->nodeModelName(), parentNode, errorMessage, nodeUse, QString(), - QStringList(n->nodeModelName()), false)) + n->modelName(), n->model(), n->proto(), QStringList(), false)) return false; subNodes << n; } @@ -477,7 +479,7 @@ bool WbDictionary::checkBoundingObjectConstraints(const WbBaseNode *defNode, QSt const WbNode *const n = mfnode->item(j); if (n) { if (!WbNodeUtilities::isAllowedToInsert(fields[i], n->nodeModelName(), parentNode, errorMessage, nodeUse, - QString(), QStringList(n->nodeModelName()), false)) + QString(), n->modelName(), n->model(), n->proto(), QStringList(), false)) return false; subNodes << n; @@ -536,7 +538,7 @@ bool WbDictionary::isSuitable(const WbNode *defNode, const QString &type) const QString errorMessage; const WbNode::NodeUse targetNodeUse = static_cast(mTargetNode)->nodeUse(); if (!WbNodeUtilities::isAllowedToInsert(mTargetField, defNode->nodeModelName(), mTargetNode, errorMessage, targetNodeUse, - type, QStringList(defNode->nodeModelName()), true)) + type, defNode->modelName(), defNode->model(), defNode->proto(), QStringList(), true)) return false; const WbBaseNode *defBaseNode = dynamic_cast(defNode); diff --git a/src/webots/nodes/utils/WbNodeOperations.cpp b/src/webots/nodes/utils/WbNodeOperations.cpp index c566770613a..10aa38649b2 100644 --- a/src/webots/nodes/utils/WbNodeOperations.cpp +++ b/src/webots/nodes/utils/WbNodeOperations.cpp @@ -183,8 +183,8 @@ WbNodeOperations::OperationResult WbNodeOperations::importNode(WbNode *parentNod childNode = static_cast(node); QString errorMessage; if (WbNodeUtilities::isAllowedToInsert(field, childNode->nodeModelName(), parentNode, errorMessage, nodeUse, - WbNodeUtilities::slotType(childNode), - QStringList() << childNode->nodeModelName() << childNode->modelName(), false)) { + WbNodeUtilities::slotType(childNode), childNode->modelName(), childNode->model(), + childNode->proto(), QStringList(), false)) { if (avoidIntersections) tryToAvoidIntersections(childNode); const OperationResult result = initNewNode(childNode, parentNode, field, nodeIndex, true); diff --git a/src/webots/nodes/utils/WbNodeUtilities.cpp b/src/webots/nodes/utils/WbNodeUtilities.cpp index 27381ab913c..82bbbe0c832 100644 --- a/src/webots/nodes/utils/WbNodeUtilities.cpp +++ b/src/webots/nodes/utils/WbNodeUtilities.cpp @@ -584,14 +584,14 @@ namespace { return dynamic_cast(node); } - bool doesFieldRestrictionAcceptNode(const WbField *const field, const QStringList &nodeNames) { + bool doesFieldRestrictionAcceptNode(const WbField *const field, const QString &nodeModelName, const WbNodeModel *nodeModel, const WbProtoModel *protoModel, const QStringList &protoParentList) { assert(field->hasRestrictedValues()); - foreach (const WbVariant variant, field->acceptedValues()) { - if (variant.type() != WB_SF_NODE) + foreach (const WbFieldValueRestriction restriction, field->acceptedValues()) { + if(restriction.type() != WB_SF_NODE) continue; - const WbNode *acceptedNode = variant.toNode(); - assert(acceptedNode); - if (nodeNames.contains(acceptedNode->modelName())) + if (restriction.isProtoNodeTypeAccepted(protoModel) || restriction.isBaseNodeTypeAccepted(nodeModel) || + (!protoModel && (restriction.toNode()->modelName() == nodeModelName || + (restriction.allowsSubtypes() && protoParentList.contains(restriction.toNode()->modelName()))))) return true; } return false; @@ -1503,16 +1503,18 @@ bool WbNodeUtilities::validateExistingChildNode(const WbField *const field, cons bool WbNodeUtilities::isAllowedToInsert(const WbField *const field, const QString &nodeName, const WbNode *node, QString &errorMessage, WbNode::NodeUse nodeUse, const QString &type, - const QStringList &restrictionValidNodeNames, bool automaticBoundingObjectCheck) { - if (field->hasRestrictedValues() && !doesFieldRestrictionAcceptNode(field, restrictionValidNodeNames)) + const QString &newNodeModel, const WbNodeModel *newNodeBaseModel, + const WbProtoModel *newNodeProtoModel, const QStringList &newNodeProtoParentList, + bool automaticBoundingObjectCheck) { + if (field->hasRestrictedValues() && !doesFieldRestrictionAcceptNode(field, newNodeModel, newNodeBaseModel, newNodeProtoModel, newNodeProtoParentList)) return false; if (field->isParameter()) { - bool valid = true; foreach (WbField *internalField, field->internalFields()) { + bool valid; if (internalField->isParameter()) // recursive call: check only node field names and not parameter names valid = isAllowedToInsert(internalField, nodeName, internalField->parentNode(), errorMessage, WbNode::UNKNOWN_USE, type, - restrictionValidNodeNames, automaticBoundingObjectCheck); + newNodeModel, newNodeBaseModel, newNodeProtoModel, newNodeProtoParentList, automaticBoundingObjectCheck); else { const WbNode *parentNode = internalField->parentNode(); valid = ::isAllowedToInsert(internalField->name(), nodeName, parentNode, errorMessage, @@ -1521,7 +1523,7 @@ bool WbNodeUtilities::isAllowedToInsert(const WbField *const field, const QStrin if (!valid) return false; } - return valid; + return true; } else return ::isAllowedToInsert(field->name(), nodeName, node, errorMessage, nodeUse, type, automaticBoundingObjectCheck); } diff --git a/src/webots/nodes/utils/WbNodeUtilities.hpp b/src/webots/nodes/utils/WbNodeUtilities.hpp index 87969f605f1..27ca4f59b7e 100644 --- a/src/webots/nodes/utils/WbNodeUtilities.hpp +++ b/src/webots/nodes/utils/WbNodeUtilities.hpp @@ -172,7 +172,8 @@ namespace WbNodeUtilities { // it first retrieve the base field and model and then check the validity // type is checked in case of Slot node bool isAllowedToInsert(const WbField *const field, const QString &nodeName, const WbNode *node, QString &errorMessage, - WbNode::NodeUse nodeUse, const QString &type, const QStringList &restrictionValidNodeNames, + WbNode::NodeUse nodeUse, const QString &type, const QString &newNodeModel, const WbNodeModel *newNodeBaseModel, + const WbProtoModel *newNodeProtoModel, const QStringList &newNodeProtoParentList = QStringList(), bool automaticBoundingObjectCheck = true); // check existing node structure diff --git a/src/webots/nodes/utils/WbWorld.cpp b/src/webots/nodes/utils/WbWorld.cpp index 2e12687f4d0..8612521c40a 100644 --- a/src/webots/nodes/utils/WbWorld.cpp +++ b/src/webots/nodes/utils/WbWorld.cpp @@ -136,7 +136,7 @@ WbWorld::WbWorld(WbTokenizer *tokenizer) : } QString errorMessage; if (WbNodeUtilities::isAllowedToInsert(childrenField, node->nodeModelName(), mRoot, errorMessage, WbNode::STRUCTURE_USE, - WbNodeUtilities::slotType(node), QStringList(node->nodeModelName()))) { + WbNodeUtilities::slotType(node), node->modelName(), node->model(), node->proto())) { node->validate(); mRoot->addChild(node); } else diff --git a/src/webots/scene_tree/WbAddNodeDialog.cpp b/src/webots/scene_tree/WbAddNodeDialog.cpp index 7964bffd1b3..5d3218840f3 100644 --- a/src/webots/scene_tree/WbAddNodeDialog.cpp +++ b/src/webots/scene_tree/WbAddNodeDialog.cpp @@ -395,11 +395,9 @@ void WbAddNodeDialog::showNodeInfo(const QString &nodeFileName, NodeType nodeTyp setPixmap(pixmapPath); } -bool WbAddNodeDialog::doFieldRestrictionsAllowNode(const QString &nodeName) const { - foreach (const WbVariant variant, mField->acceptedValues()) { - const WbNode *node = variant.toNode(); - assert(node); - if (node->modelName() == nodeName) +bool WbAddNodeDialog::doFieldRestrictionsAllowNode(const WbNode *node) const { + foreach (const WbFieldValueRestriction restriction, mField->acceptedValues()) { + if (restriction.isNodeAccepted(node)) return true; } return false; @@ -438,7 +436,7 @@ void WbAddNodeDialog::buildTree() { QString errorMessage; if (fileInfo.baseName().contains(regexp) && WbNodeUtilities::isAllowedToInsert(mField, fileInfo.baseName(), mCurrentNode, errorMessage, nodeUse, QString(), - QStringList(fileInfo.baseName()))) { + fileInfo.baseName(), WbNodeModel::findModel(fileInfo.baseName()), NULL)) { item = new QTreeWidgetItem(nodesItem, QStringList(fileInfo.baseName())); item->setIcon(0, QIcon("enabledIcons:node.png")); nodesItem->addChild(item); @@ -463,8 +461,7 @@ void WbAddNodeDialog::buildTree() { const QString ¤tFullDefName = currentDefName + " (" + currentModelName + ")"; if (!currentFullDefName.contains(regexp)) continue; - if (mField->hasRestrictedValues() && - (!doFieldRestrictionsAllowNode(currentModelName) && !doFieldRestrictionsAllowNode(node->nodeModelName()))) + if (mField->hasRestrictedValues() && !doFieldRestrictionsAllowNode(node)) continue; QString nodeFilePath(currentModelName); if (!WbNodeModel::isBaseModelName(currentModelName)) { @@ -562,7 +559,7 @@ int WbAddNodeDialog::addProtosFromProtoList(QTreeWidgetItem *parentItem, int typ QString errorMessage; const QString nodeName = it.key(); if (!WbNodeUtilities::isAllowedToInsert(mField, baseType, mCurrentNode, errorMessage, nodeUse, info->slotType(), - QStringList() << baseType << nodeName)) + nodeName, WbNodeModel::findModel(baseType), NULL, info->parents())) continue; // keep track of unique local proto that may clash diff --git a/src/webots/scene_tree/WbAddNodeDialog.hpp b/src/webots/scene_tree/WbAddNodeDialog.hpp index c7f27e4cf81..5d642d73c02 100644 --- a/src/webots/scene_tree/WbAddNodeDialog.hpp +++ b/src/webots/scene_tree/WbAddNodeDialog.hpp @@ -88,7 +88,7 @@ private slots: int addProtos(QTreeWidgetItem *parentItem, const QStringList &protoList, const QString &dirPath, const QRegularExpression ®exp, const QDir &rootDirectory); void showNodeInfo(const QString &nodeFileName, NodeType nodeType, int variant = -1, const QString &boundingObjectInfo = ""); - bool doFieldRestrictionsAllowNode(const QString &nodeName) const; + bool doFieldRestrictionsAllowNode(const WbNode *node) const; bool isDeclarationConflicting(const QString &protoName, const QString &url); diff --git a/src/webots/scene_tree/WbSceneTree.cpp b/src/webots/scene_tree/WbSceneTree.cpp index dfc7d50af95..08eb14bb660 100644 --- a/src/webots/scene_tree/WbSceneTree.cpp +++ b/src/webots/scene_tree/WbSceneTree.cpp @@ -1071,7 +1071,7 @@ bool WbSceneTree::isPasteAllowed() { QString errorMessage; if (!WbNodeUtilities::isAllowedToInsert(field, nodeModelName, parentNode, errorMessage, static_cast(parentNode)->nodeUse(), clipboardNodeInfo->slotType, - QStringList() << nodeModelName << clipboardNodeInfo->modelName)) + clipboardNodeInfo->modelName, WbNodeModel::findModel(nodeModelName), NULL, clipboardNodeInfo->protoParentList)) return false; if (clipboardNodeInfo->hasADeviceDescendant) diff --git a/src/webots/scene_tree/WbValueEditor.cpp b/src/webots/scene_tree/WbValueEditor.cpp index 2c6eb660745..8c7df3af9f1 100644 --- a/src/webots/scene_tree/WbValueEditor.cpp +++ b/src/webots/scene_tree/WbValueEditor.cpp @@ -61,10 +61,10 @@ void WbValueEditor::edit(WbNode *node, WbField *field, int index) { mComboBox->clear(); if (mField->singleType() != WB_SF_NODE && mField->hasRestrictedValues()) { if (mField->singleType() != WB_SF_STRING) { - foreach (const WbVariant acceptedVariant, mField->acceptedValues()) + foreach (const WbFieldValueRestriction acceptedVariant, mField->acceptedValues()) mComboBox->addItem(acceptedVariant.toSimplifiedStringRepresentation()); } else { // In case of MF/SF_STRING we don't want to display the starting and ending '"' - foreach (const WbVariant acceptedVariant, mField->acceptedValues()) + foreach (const WbFieldValueRestriction acceptedVariant, mField->acceptedValues()) mComboBox->addItem(acceptedVariant.toSimplifiedStringRepresentation().chopped(1).remove(0, 1)); } connect(mComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(apply()), Qt::UniqueConnection); diff --git a/src/webots/user_commands/WbClipboard.cpp b/src/webots/user_commands/WbClipboard.cpp index df2ed8d560b..d01a20fb4da 100644 --- a/src/webots/user_commands/WbClipboard.cpp +++ b/src/webots/user_commands/WbClipboard.cpp @@ -19,6 +19,7 @@ #include "WbNodeUtilities.hpp" #include "WbRgb.hpp" #include "WbRotation.hpp" +#include "WbProtoModel.hpp" #include "WbVector2.hpp" #include "WbVector3.hpp" #include "WbVrmlNodeUtilities.hpp" @@ -151,6 +152,7 @@ void WbClipboard::setNode(WbNode *n, bool persistent) { mNodeInfo = new WbClipboardNodeInfo(); mNodeInfo->modelName = n->modelName(); mNodeInfo->nodeModelName = n->nodeModelName(); + mNodeInfo->protoParentList = n->proto() ? n->proto()->parentList() : QStringList(); mNodeInfo->slotType = WbNodeUtilities::slotType(n); mNodeInfo->hasADeviceDescendant = WbNodeUtilities::hasADeviceDescendant(n, true); mNodeInfo->hasAConnectorDescendant = mNodeInfo->hasADeviceDescendant || WbNodeUtilities::hasADeviceDescendant(n, false); diff --git a/src/webots/user_commands/WbClipboard.hpp b/src/webots/user_commands/WbClipboard.hpp index 3b540a3f0af..f05645ba3e0 100644 --- a/src/webots/user_commands/WbClipboard.hpp +++ b/src/webots/user_commands/WbClipboard.hpp @@ -56,6 +56,7 @@ class WbClipboard : public WbVariant { struct WbClipboardNodeInfo { QString modelName; QString nodeModelName; + QStringList protoParentList; QString slotType; bool hasADeviceDescendant; bool hasAConnectorDescendant; diff --git a/src/webots/vrml/WbField.cpp b/src/webots/vrml/WbField.cpp index ddf6f0dfca1..d69cc5d68af 100644 --- a/src/webots/vrml/WbField.cpp +++ b/src/webots/vrml/WbField.cpp @@ -166,7 +166,7 @@ void WbField::checkValueIsAccepted() { int refusedIndex; if (!mModel->isValueAccepted(mValue, &refusedIndex)) { QString acceptedValuesList = ""; - foreach (const WbVariant acceptedValue, mModel->acceptedValues()) + foreach (const WbFieldValueRestriction acceptedValue, mModel->acceptedValues()) acceptedValuesList += acceptedValue.toSimplifiedStringRepresentation() + ", "; acceptedValuesList.chop(2); QString error; diff --git a/src/webots/vrml/WbField.hpp b/src/webots/vrml/WbField.hpp index 016b21cbade..f10389ad227 100644 --- a/src/webots/vrml/WbField.hpp +++ b/src/webots/vrml/WbField.hpp @@ -107,7 +107,7 @@ class WbField : public QObject { // accepted values bool hasRestrictedValues() const { return mModel->hasRestrictedValues(); } - const QList acceptedValues() const { return mModel->acceptedValues(); } + const QList acceptedValues() const { return mModel->acceptedValues(); } // enable forwarding signals when the size of MF fields changes void listenToValueSizeChanges() const; From b2917aa66ae7d48037b1d6297745910c2c45d754 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sat, 6 Jul 2024 19:21:47 -0700 Subject: [PATCH 20/37] switch to '+' syntax --- src/webots/vrml/WbFieldModel.cpp | 14 ++++---------- src/webots/vrml/WbParser.cpp | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/webots/vrml/WbFieldModel.cpp b/src/webots/vrml/WbFieldModel.cpp index 1da050f7a1c..c327551ecb9 100644 --- a/src/webots/vrml/WbFieldModel.cpp +++ b/src/webots/vrml/WbFieldModel.cpp @@ -182,20 +182,14 @@ QList WbFieldModel::getAcceptedValues(const QString &ty while (tokenizer->nextWord() != '}') { tokenizer->ungetToken(); - bool allowSubtypeMatch; - if (tokenizer->fileVersion() >= WbVersion(2024, 0, 0)) { - if(tokenizer->peekWord() == '=') { - tokenizer->nextToken(); - allowSubtypeMatch = false; - } else - allowSubtypeMatch = true; - } else - allowSubtypeMatch = false; - const WbSingleValue *singleValue = dynamic_cast(WbFieldModel::createValueForVrmlType(type, tokenizer, worldPath)); assert(singleValue); + bool allowSubtypeMatch = tokenizer->peekWord() == '+'; + if(allowSubtypeMatch) + tokenizer->nextToken(); + WbFieldValueRestriction restriction(singleValue->variantValue(), allowSubtypeMatch); if (type == "SFNode" && restriction.toNode()) { // explicit copy of the node to be persistent. diff --git a/src/webots/vrml/WbParser.cpp b/src/webots/vrml/WbParser.cpp index cdafa088f6c..d2ffc32b5dd 100644 --- a/src/webots/vrml/WbParser.cpp +++ b/src/webots/vrml/WbParser.cpp @@ -150,9 +150,9 @@ void WbParser::parseFieldValue(WbFieldType type, const QString &worldPath) { void WbParser::parseFieldAcceptedValues(WbFieldType type, const QString &worldPath) { while (nextWord() != '}') { mTokenizer->ungetToken(); - if (nextWord() != '=') - mTokenizer->ungetToken(); WbParser::parseSingleFieldValue(type, worldPath); + if (nextWord() != '+') + mTokenizer->ungetToken(); } } From cef8dfd4e4a6d246536490b1fda9cf2d802e9607 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sat, 6 Jul 2024 22:27:19 -0700 Subject: [PATCH 21/37] check if old field validation behavior should be used --- src/webots/vrml/WbFieldValueRestriction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webots/vrml/WbFieldValueRestriction.cpp b/src/webots/vrml/WbFieldValueRestriction.cpp index 83bf0732c9b..9d3cf68972d 100644 --- a/src/webots/vrml/WbFieldValueRestriction.cpp +++ b/src/webots/vrml/WbFieldValueRestriction.cpp @@ -52,11 +52,11 @@ bool WbFieldValueRestriction::isNodeAccepted(const WbNode *node) const { bool WbFieldValueRestriction::isBaseNodeTypeAccepted(const WbNodeModel *actualType) const { if (type() != WB_SF_NODE || !actualType) return false; - return toNode()->modelName() == actualType->name() || isBaseNodeTypeAccepted(actualType->parentModel()); + return toNode()->modelName() == actualType->name() || (allowsSubtypes() && isBaseNodeTypeAccepted(actualType->parentModel())); } bool WbFieldValueRestriction::isProtoNodeTypeAccepted(const WbProtoModel *actualType) const { if (type() != WB_SF_NODE || !actualType) return false; - return toNode()->modelName() == actualType->name() || isProtoNodeTypeAccepted(actualType->ancestorProtoModel()); + return toNode()->modelName() == actualType->name() || (allowsSubtypes() && isProtoNodeTypeAccepted(actualType->ancestorProtoModel())); } From 5261ff9e34922f1d398dffe9dc5dc0b7c01aea02 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sat, 6 Jul 2024 22:27:35 -0700 Subject: [PATCH 22/37] fix parsing of '+' --- src/webots/vrml/WbToken.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webots/vrml/WbToken.cpp b/src/webots/vrml/WbToken.cpp index 4de9af6af30..cdfe629b760 100644 --- a/src/webots/vrml/WbToken.cpp +++ b/src/webots/vrml/WbToken.cpp @@ -39,7 +39,8 @@ WbToken::WbToken(const QString &word, int line, int column) : mLine(line), mColu bool ok; // cppcheck-suppress ignoredReturnValue word.toDouble(&ok); - mType = ok ? NUMERIC : INVALID; + // "+" on its own is a punctuation mark + mType = ok ? NUMERIC : word == "+" ? PUNCTUATION : INVALID; } else if (isKeyword(word)) mType = KEYWORD; else if (isValidIdentifier(word)) From 17ae94ea4d537bfdcc1d7e087b658f56907ea4e1 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:31:05 -0700 Subject: [PATCH 23/37] update tests --- tests/manual_tests/protos/PoseProto.proto | 4 +- tests/parser/expected_results.txt | 1 + .../protos/ProtoRestrictedFieldValues.proto | 3 +- .../worlds/proto_allowed_mf_field_value.wbt | 5 +++ ...t_allowed_mf_field_value_base_of_proto.wbt | 41 +++++++++++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tests/parser/worlds/proto_not_allowed_mf_field_value_base_of_proto.wbt diff --git a/tests/manual_tests/protos/PoseProto.proto b/tests/manual_tests/protos/PoseProto.proto index 3fff815c07b..4900e1cd392 100644 --- a/tests/manual_tests/protos/PoseProto.proto +++ b/tests/manual_tests/protos/PoseProto.proto @@ -6,7 +6,7 @@ PROTO PoseProto [ ] { Pose { - translation IS translation - rotation IS rotation + translation IS translation + rotation IS rotation } } diff --git a/tests/parser/expected_results.txt b/tests/parser/expected_results.txt index 3f1cf618856..82994463181 100644 --- a/tests/parser/expected_results.txt +++ b/tests/parser/expected_results.txt @@ -27,6 +27,7 @@ parser/worlds/proto_missing_item_value_in_field.wbt "Expected floating point val parser/worlds/proto_nested_parameter.wbt VOID parser/worlds/proto_not_allowed_field_value.wbt "Invalid 'translation' changed to 0 0 0. The value should be in the list: {0 0 0}." parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Pose' removed from 'extensionSlot' field. The values should be in the list: {Solid, PoseProto}." +parser/worlds/proto_not_allowed_mf_field_value_base_of_proto.wbt "Invalid 'Pose' removed from 'extensionSlot2' field. The values should be in the list: {Solid, PoseProto}." parser/worlds/proto_not_allowed_mf_field_value_proto.wbt "Invalid 'Parameter' removed from 'extensionSlot' field. The values should be in the list: {Solid, PoseProto}." parser/worlds/proto_parameter_missing_ending_curly_bracket.wbt "Expected field name or '}', found ']'." parser/worlds/proto_typo_in_alias.wbt "PROTO parameter 'rotation' has no matching IS field." diff --git a/tests/parser/protos/ProtoRestrictedFieldValues.proto b/tests/parser/protos/ProtoRestrictedFieldValues.proto index ca6c0546fe9..81dc663b59e 100644 --- a/tests/parser/protos/ProtoRestrictedFieldValues.proto +++ b/tests/parser/protos/ProtoRestrictedFieldValues.proto @@ -6,7 +6,8 @@ PROTO ProtoRestrictedFieldValues [ field SFRotation{0 1 0 0, 0 0 1 0, 0 1 0 1.5708} rotation 0 0 1 0 field SFVec3f{0 0 0} translation 0 0 0 - field MFNode{Solid{}, PoseProto{}} extensionSlot [] + field MFNode{Solid{}+, PoseProto{}+} extensionSlot [] + field MFNode{PoseProto{}} extensionSlot2 [] ] { Solid { diff --git a/tests/parser/worlds/proto_allowed_mf_field_value.wbt b/tests/parser/worlds/proto_allowed_mf_field_value.wbt index 7b23391de7b..150dd13d44a 100644 --- a/tests/parser/worlds/proto_allowed_mf_field_value.wbt +++ b/tests/parser/worlds/proto_allowed_mf_field_value.wbt @@ -1,6 +1,7 @@ #VRML_SIM R2024a utf8 EXTERNPROTO "webots://tests/manual_tests/protos/DerivedPoseProto.proto" +EXTERNPROTO "webots://tests/manual_tests/protos/PoseProto.proto" EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" @@ -37,4 +38,8 @@ ProtoRestrictedFieldValues { Robot { } ] + extensonSlot2 { + PoseProto { + } + } } diff --git a/tests/parser/worlds/proto_not_allowed_mf_field_value_base_of_proto.wbt b/tests/parser/worlds/proto_not_allowed_mf_field_value_base_of_proto.wbt new file mode 100644 index 00000000000..2411cfbc3df --- /dev/null +++ b/tests/parser/worlds/proto_not_allowed_mf_field_value_base_of_proto.wbt @@ -0,0 +1,41 @@ +#VRML_SIM R2024a utf8 + +EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" +EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" +EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" + +WorldInfo { +} +Viewpoint { + orientation 0.7470290283624516 0.646616024550071 0.15438700586161153 5.42889 + position -1.2077 1.6424 1.96945 +} +Background { + skyColor [ + 0.4 0.7 1 + ] +} +ParserTestSupervisor { +} +DirectionalLight { + ambientIntensity 1 + direction -0.33 -1 -0.5 + castShadows TRUE +} +Floor { + rotation 1 0 0 -1.5708 +} +ProtoRestrictedFieldValues { + rotation 0 1 0 1.5708 + translation 0 0 0 + extensionSlot [ + DerivedPoseProto { + } + Solid { + } + ] + extensionSlot2 [ + Pose { + } + ] +} From 1625d8caa8caf13a858a90c38d0f2010d302f533 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:33:45 -0700 Subject: [PATCH 24/37] clarify error messages to indicate whether subtypes are allowed --- src/webots/vrml/WbField.cpp | 2 +- tests/parser/expected_results.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/webots/vrml/WbField.cpp b/src/webots/vrml/WbField.cpp index d69cc5d68af..69dc822c475 100644 --- a/src/webots/vrml/WbField.cpp +++ b/src/webots/vrml/WbField.cpp @@ -167,7 +167,7 @@ void WbField::checkValueIsAccepted() { if (!mModel->isValueAccepted(mValue, &refusedIndex)) { QString acceptedValuesList = ""; foreach (const WbFieldValueRestriction acceptedValue, mModel->acceptedValues()) - acceptedValuesList += acceptedValue.toSimplifiedStringRepresentation() + ", "; + acceptedValuesList += acceptedValue.toSimplifiedStringRepresentation() + (acceptedValue->allowsSubtypes() ? "+" : "") + ", "; acceptedValuesList.chop(2); QString error; if (isSingle()) { diff --git a/tests/parser/expected_results.txt b/tests/parser/expected_results.txt index 82994463181..000c9d342f1 100644 --- a/tests/parser/expected_results.txt +++ b/tests/parser/expected_results.txt @@ -26,9 +26,9 @@ parser/worlds/proto_missing_ending_curly_bracket.wbt "Expected field name or '}' parser/worlds/proto_missing_item_value_in_field.wbt "Expected floating point value, found 'field'." parser/worlds/proto_nested_parameter.wbt VOID parser/worlds/proto_not_allowed_field_value.wbt "Invalid 'translation' changed to 0 0 0. The value should be in the list: {0 0 0}." -parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Pose' removed from 'extensionSlot' field. The values should be in the list: {Solid, PoseProto}." -parser/worlds/proto_not_allowed_mf_field_value_base_of_proto.wbt "Invalid 'Pose' removed from 'extensionSlot2' field. The values should be in the list: {Solid, PoseProto}." -parser/worlds/proto_not_allowed_mf_field_value_proto.wbt "Invalid 'Parameter' removed from 'extensionSlot' field. The values should be in the list: {Solid, PoseProto}." +parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Pose' removed from 'extensionSlot' field. The values should be in the list: {Solid+, PoseProto+}." +parser/worlds/proto_not_allowed_mf_field_value_base_of_proto.wbt "Invalid 'Pose' removed from 'extensionSlot2' field. The values should be in the list: {PoseProto}." +parser/worlds/proto_not_allowed_mf_field_value_proto.wbt "Invalid 'Parameter' removed from 'extensionSlot' field. The values should be in the list: {Solid+, PoseProto+}." parser/worlds/proto_parameter_missing_ending_curly_bracket.wbt "Expected field name or '}', found ']'." parser/worlds/proto_typo_in_alias.wbt "PROTO parameter 'rotation' has no matching IS field." parser/worlds/proto_typo_in_is_keyword.wbt "Expected floating point value, found 'ISNOT'." From c12c7431c54d0f5392e428219dd976b4e20ac72d Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:39:28 -0700 Subject: [PATCH 25/37] more tests --- tests/parser/expected_results.txt | 2 + .../protos/ProtoRestrictedFieldValues.proto | 1 + .../worlds/proto_allowed_mf_field_value.wbt | 8 +++- ...llowed_mf_field_value_base_no_subtypes.wbt | 42 +++++++++++++++++++ ...lowed_mf_field_value_proto_no_subtypes.wbt | 42 +++++++++++++++++++ 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tests/parser/worlds/proto_not_allowed_mf_field_value_base_no_subtypes.wbt create mode 100644 tests/parser/worlds/proto_not_allowed_mf_field_value_proto_no_subtypes.wbt diff --git a/tests/parser/expected_results.txt b/tests/parser/expected_results.txt index 000c9d342f1..b98c5349577 100644 --- a/tests/parser/expected_results.txt +++ b/tests/parser/expected_results.txt @@ -27,8 +27,10 @@ parser/worlds/proto_missing_item_value_in_field.wbt "Expected floating point val parser/worlds/proto_nested_parameter.wbt VOID parser/worlds/proto_not_allowed_field_value.wbt "Invalid 'translation' changed to 0 0 0. The value should be in the list: {0 0 0}." parser/worlds/proto_not_allowed_mf_field_value_base.wbt "Invalid 'Pose' removed from 'extensionSlot' field. The values should be in the list: {Solid+, PoseProto+}." +parser/worlds/proto_not_allowed_mf_field_value_base_no_subtypes.wbt "Invalid 'Solid' removed from 'extensionSlot3' field. The values should be in the list: {Pose}." parser/worlds/proto_not_allowed_mf_field_value_base_of_proto.wbt "Invalid 'Pose' removed from 'extensionSlot2' field. The values should be in the list: {PoseProto}." parser/worlds/proto_not_allowed_mf_field_value_proto.wbt "Invalid 'Parameter' removed from 'extensionSlot' field. The values should be in the list: {Solid+, PoseProto+}." +parser/worlds/proto_not_allowed_mf_field_value_proto_no_subtypes.wbt "Invalid 'DerivedPoseProto' removed from 'extensionSlot2' field. The values should be in the list: {PoseProto}." parser/worlds/proto_parameter_missing_ending_curly_bracket.wbt "Expected field name or '}', found ']'." parser/worlds/proto_typo_in_alias.wbt "PROTO parameter 'rotation' has no matching IS field." parser/worlds/proto_typo_in_is_keyword.wbt "Expected floating point value, found 'ISNOT'." diff --git a/tests/parser/protos/ProtoRestrictedFieldValues.proto b/tests/parser/protos/ProtoRestrictedFieldValues.proto index 81dc663b59e..c182f04c798 100644 --- a/tests/parser/protos/ProtoRestrictedFieldValues.proto +++ b/tests/parser/protos/ProtoRestrictedFieldValues.proto @@ -8,6 +8,7 @@ PROTO ProtoRestrictedFieldValues field SFVec3f{0 0 0} translation 0 0 0 field MFNode{Solid{}+, PoseProto{}+} extensionSlot [] field MFNode{PoseProto{}} extensionSlot2 [] + field MFNode{Pose{}} extensionSlot3 [] ] { Solid { diff --git a/tests/parser/worlds/proto_allowed_mf_field_value.wbt b/tests/parser/worlds/proto_allowed_mf_field_value.wbt index 150dd13d44a..b00693ae69f 100644 --- a/tests/parser/worlds/proto_allowed_mf_field_value.wbt +++ b/tests/parser/worlds/proto_allowed_mf_field_value.wbt @@ -38,8 +38,12 @@ ProtoRestrictedFieldValues { Robot { } ] - extensonSlot2 { + extensonSlot2 [ PoseProto { } - } + ] + extensionSlot3 [ + DerivedPoseProto { + } + ] } diff --git a/tests/parser/worlds/proto_not_allowed_mf_field_value_base_no_subtypes.wbt b/tests/parser/worlds/proto_not_allowed_mf_field_value_base_no_subtypes.wbt new file mode 100644 index 00000000000..328aa69dbdd --- /dev/null +++ b/tests/parser/worlds/proto_not_allowed_mf_field_value_base_no_subtypes.wbt @@ -0,0 +1,42 @@ +#VRML_SIM R2024a utf8 + +EXTERNPROTO "webots://tests/manual_tests/protos/DerivedPoseProto.proto" +EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" +EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" +EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" + +WorldInfo { +} +Viewpoint { + orientation 0.7470290283624516 0.646616024550071 0.15438700586161153 5.42889 + position -1.2077 1.6424 1.96945 +} +Background { + skyColor [ + 0.4 0.7 1 + ] +} +ParserTestSupervisor { +} +DirectionalLight { + ambientIntensity 1 + direction -0.33 -1 -0.5 + castShadows TRUE +} +Floor { + rotation 1 0 0 -1.5708 +} +ProtoRestrictedFieldValues { + rotation 0 1 0 1.5708 + translation 0 0 0 + extensionSlot [ + DerivedPoseProto { + } + Solid { + } + ], + extensionSlot3 [ + Solid { + } + ] +} diff --git a/tests/parser/worlds/proto_not_allowed_mf_field_value_proto_no_subtypes.wbt b/tests/parser/worlds/proto_not_allowed_mf_field_value_proto_no_subtypes.wbt new file mode 100644 index 00000000000..e7929f94b24 --- /dev/null +++ b/tests/parser/worlds/proto_not_allowed_mf_field_value_proto_no_subtypes.wbt @@ -0,0 +1,42 @@ +#VRML_SIM R2024a utf8 + +EXTERNPROTO "webots://tests/manual_tests/protos/DerivedPoseProto.proto" +EXTERNPROTO "webots://tests/parser/protos/ParserTestSupervisor.proto" +EXTERNPROTO "webots://projects/objects/floors/protos/Floor.proto" +EXTERNPROTO "webots://tests/parser/protos/ProtoRestrictedFieldValues.proto" + +WorldInfo { +} +Viewpoint { + orientation 0.7470290283624516 0.646616024550071 0.15438700586161153 5.42889 + position -1.2077 1.6424 1.96945 +} +Background { + skyColor [ + 0.4 0.7 1 + ] +} +ParserTestSupervisor { +} +DirectionalLight { + ambientIntensity 1 + direction -0.33 -1 -0.5 + castShadows TRUE +} +Floor { + rotation 1 0 0 -1.5708 +} +ProtoRestrictedFieldValues { + rotation 0 1 0 1.5708 + translation 0 0 0 + extensionSlot [ + DerivedPoseProto { + } + Solid { + } + ], + extensionSlot2 [ + DerivedPoseProto { + } + ] +} From 9246e3529d9bf5ecf6d4a9902378969743241b9f Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:47:13 -0700 Subject: [PATCH 26/37] bug fixes --- src/webots/vrml/WbField.cpp | 2 +- src/webots/vrml/WbFieldModel.cpp | 1 - tests/parser/protos/ProtoRestrictedFieldValues.proto | 4 ++-- tests/parser/worlds/proto_allowed_mf_field_value.wbt | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/webots/vrml/WbField.cpp b/src/webots/vrml/WbField.cpp index 69dc822c475..29dd310a327 100644 --- a/src/webots/vrml/WbField.cpp +++ b/src/webots/vrml/WbField.cpp @@ -167,7 +167,7 @@ void WbField::checkValueIsAccepted() { if (!mModel->isValueAccepted(mValue, &refusedIndex)) { QString acceptedValuesList = ""; foreach (const WbFieldValueRestriction acceptedValue, mModel->acceptedValues()) - acceptedValuesList += acceptedValue.toSimplifiedStringRepresentation() + (acceptedValue->allowsSubtypes() ? "+" : "") + ", "; + acceptedValuesList += acceptedValue.toSimplifiedStringRepresentation() + (acceptedValue.allowsSubtypes() ? "+" : "") + ", "; acceptedValuesList.chop(2); QString error; if (isSingle()) { diff --git a/src/webots/vrml/WbFieldModel.cpp b/src/webots/vrml/WbFieldModel.cpp index c327551ecb9..1347297a9cc 100644 --- a/src/webots/vrml/WbFieldModel.cpp +++ b/src/webots/vrml/WbFieldModel.cpp @@ -38,7 +38,6 @@ #include "WbToken.hpp" #include "WbTokenizer.hpp" #include "WbValue.hpp" -#include "WbVersion.hpp" #include "WbWriter.hpp" #include diff --git a/tests/parser/protos/ProtoRestrictedFieldValues.proto b/tests/parser/protos/ProtoRestrictedFieldValues.proto index c182f04c798..d30513d8985 100644 --- a/tests/parser/protos/ProtoRestrictedFieldValues.proto +++ b/tests/parser/protos/ProtoRestrictedFieldValues.proto @@ -7,8 +7,8 @@ PROTO ProtoRestrictedFieldValues field SFRotation{0 1 0 0, 0 0 1 0, 0 1 0 1.5708} rotation 0 0 1 0 field SFVec3f{0 0 0} translation 0 0 0 field MFNode{Solid{}+, PoseProto{}+} extensionSlot [] - field MFNode{PoseProto{}} extensionSlot2 [] - field MFNode{Pose{}} extensionSlot3 [] + unconnectedField MFNode{PoseProto{}} extensionSlot2 [] + unconnectedField MFNode{Pose{}} extensionSlot3 [] ] { Solid { diff --git a/tests/parser/worlds/proto_allowed_mf_field_value.wbt b/tests/parser/worlds/proto_allowed_mf_field_value.wbt index b00693ae69f..7a72a77f2f6 100644 --- a/tests/parser/worlds/proto_allowed_mf_field_value.wbt +++ b/tests/parser/worlds/proto_allowed_mf_field_value.wbt @@ -38,7 +38,7 @@ ProtoRestrictedFieldValues { Robot { } ] - extensonSlot2 [ + extensionSlot2 [ PoseProto { } ] From 1efc106c4558eb6254f3ccc9781d549456d50499 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:59:21 -0700 Subject: [PATCH 27/37] update docs --- docs/reference/proto-definition.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/reference/proto-definition.md b/docs/reference/proto-definition.md index e0d34add141..8bcc9081f53 100644 --- a/docs/reference/proto-definition.md +++ b/docs/reference/proto-definition.md @@ -77,11 +77,13 @@ PROTO MyProto [ field SFString name "my proto" field SFColor{0 0 0, 0.5 0.5 0.5, 1 1 1} color 0.5 0.5 0.5 field SFNode physics NULL - field MFNode{Solid{}, Transform{}} extensionSlot [] + field MFNode{Solid{}+, Pose{}} extensionSlot [] ] ``` -In this example, the `color` field value can only be `0 0 0`, `0.5 0.5 0.5` or `1 1 1` and the `extensionSlot` field can only accept [Solid](../reference/solid.md), [Transform](../reference/transform.md) nodes, and nodes that inherit from them. +For node fields, the `{}+` syntax allows the field to also accept nodes which derive from a specific node type. + +In this example, the `color` field value can only be `0 0 0`, `0.5 0.5 0.5` or `1 1 1` and the `extensionSlot` field can only accept [Pose](pose.md) nodes, PROTOs whose base type is [Pose](pose.md), [Solid](solid.md) nodes, nodes derived from [Solid](solid.md), and PROTOs derived from [Solid](solid.md) or a type that inherits from [Solid](../reference/solid.md). Note that because `Pose{}` is not followed by a `+`, `extensionSlot` does not accept nodes that derive from [Pose](pose.md) (e.g. [Transform](transform.md) or [Fluid](fluid.md)) or PROTOs whose base type is not [Pose](pose.md). ### IS Statements From bed14413352138b0df3f69ec9558b8ff146c8ad0 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:01:43 -0700 Subject: [PATCH 28/37] documentation clarification --- docs/reference/proto-definition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/proto-definition.md b/docs/reference/proto-definition.md index 8bcc9081f53..44e3b3b858d 100644 --- a/docs/reference/proto-definition.md +++ b/docs/reference/proto-definition.md @@ -81,7 +81,7 @@ PROTO MyProto [ ] ``` -For node fields, the `{}+` syntax allows the field to also accept nodes which derive from a specific node type. +For `SFNode`/`MFNode` fields, the `{}+` syntax allows the field to also accept nodes which derive from a specific node type. In this example, the `color` field value can only be `0 0 0`, `0.5 0.5 0.5` or `1 1 1` and the `extensionSlot` field can only accept [Pose](pose.md) nodes, PROTOs whose base type is [Pose](pose.md), [Solid](solid.md) nodes, nodes derived from [Solid](solid.md), and PROTOs derived from [Solid](solid.md) or a type that inherits from [Solid](../reference/solid.md). Note that because `Pose{}` is not followed by a `+`, `extensionSlot` does not accept nodes that derive from [Pose](pose.md) (e.g. [Transform](transform.md) or [Fluid](fluid.md)) or PROTOs whose base type is not [Pose](pose.md). From e355f6de2a5344a3ba21acd3f571f743d7979e7c Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:03:09 -0700 Subject: [PATCH 29/37] update changelog --- docs/reference/changelog-r2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/changelog-r2024.md b/docs/reference/changelog-r2024.md index 443dba23f11..c0853d9d6a3 100644 --- a/docs/reference/changelog-r2024.md +++ b/docs/reference/changelog-r2024.md @@ -4,9 +4,9 @@ Released on December **th, 2023. - New Features - **Change the name of the web scene format from `X3D` to `W3D` ([#6280](https://github.com/cyberbotics/webots/pull/6280)).** + - Added a method to include all subtypes of a node type in a PROTO field restriction ([#6574](https://github.com/cyberbotics/webots/pull/6574)). - Enhancements - Improved the image range of the rotating [Lidar](lidar.md) ([#6324](https://github.com/cyberbotics/webots/pull/6324)). - - Updated proto node restrictions to consider node ancestry ([#6574](https://github.com/cyberbotics/webots/pull/6574)). - Cleanup - Removed deprecated `windowPosition`, `pixelSize` fields of [Display](display.md) node ([#6327](https://github.com/cyberbotics/webots/pull/6327)). - Bug Fixes From 2960ff3c9d2a358143cbe174b611284e1ae40c30 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:13:47 -0700 Subject: [PATCH 30/37] clarify function name --- src/webots/user_commands/WbClipboard.cpp | 2 +- src/webots/vrml/WbProtoManager.cpp | 2 +- src/webots/vrml/WbProtoModel.cpp | 2 +- src/webots/vrml/WbProtoModel.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/webots/user_commands/WbClipboard.cpp b/src/webots/user_commands/WbClipboard.cpp index d01a20fb4da..529a2515733 100644 --- a/src/webots/user_commands/WbClipboard.cpp +++ b/src/webots/user_commands/WbClipboard.cpp @@ -152,7 +152,7 @@ void WbClipboard::setNode(WbNode *n, bool persistent) { mNodeInfo = new WbClipboardNodeInfo(); mNodeInfo->modelName = n->modelName(); mNodeInfo->nodeModelName = n->nodeModelName(); - mNodeInfo->protoParentList = n->proto() ? n->proto()->parentList() : QStringList(); + mNodeInfo->protoParentList = n->proto() ? n->proto()->parentProtoNames() : QStringList(); mNodeInfo->slotType = WbNodeUtilities::slotType(n); mNodeInfo->hasADeviceDescendant = WbNodeUtilities::hasADeviceDescendant(n, true); mNodeInfo->hasAConnectorDescendant = mNodeInfo->hasADeviceDescendant || WbNodeUtilities::hasADeviceDescendant(n, false); diff --git a/src/webots/vrml/WbProtoManager.cpp b/src/webots/vrml/WbProtoManager.cpp index 5eaac4112e8..1a8240b732d 100644 --- a/src/webots/vrml/WbProtoManager.cpp +++ b/src/webots/vrml/WbProtoManager.cpp @@ -761,7 +761,7 @@ WbProtoInfo *WbProtoManager::generateInfoFromProtoFile(const QString &protoFileN } // generate parents string (needed by PROTO wizard) - QStringList parents = protoModel->parentList(); + QStringList parents = protoModel->parentProtoNames(); WbProtoInfo *info = new WbProtoInfo(url, protoModel->baseType(), protoModel->license(), protoModel->licenseUrl(), protoModel->documentationUrl(), protoModel->info(), protoModel->slotType(), diff --git a/src/webots/vrml/WbProtoModel.cpp b/src/webots/vrml/WbProtoModel.cpp index 4494e107d82..8d5e3f98ebc 100644 --- a/src/webots/vrml/WbProtoModel.cpp +++ b/src/webots/vrml/WbProtoModel.cpp @@ -417,7 +417,7 @@ WbNode *WbProtoModel::generateRoot(const QVector ¶meters, const Q return root; } -QStringList WbProtoModel::parentList() const { +QStringList WbProtoModel::parentProtoNames() const { QStringList parents; const WbProtoModel *parentProtoModel = this; while ((parentProtoModel = parentProtoModel->ancestorProtoModel())) { diff --git a/src/webots/vrml/WbProtoModel.hpp b/src/webots/vrml/WbProtoModel.hpp index 232a000293c..f6de9c06d1a 100644 --- a/src/webots/vrml/WbProtoModel.hpp +++ b/src/webots/vrml/WbProtoModel.hpp @@ -97,7 +97,7 @@ class WbProtoModel : public QObject { const QString &slotType() const { return mSlotType; } - QStringList parentList() const; + QStringList parentProtoNames() const; QStringList parameterNames() const; From aca6d79c41006f0207c863b3b4441411a0fb6e01 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:21:07 -0700 Subject: [PATCH 31/37] run clang-format --- src/webots/nodes/utils/WbDictionary.cpp | 11 ++++--- src/webots/nodes/utils/WbNodeUtilities.cpp | 17 +++++------ src/webots/nodes/utils/WbNodeUtilities.hpp | 6 ++-- src/webots/nodes/utils/WbWorld.cpp | 3 +- src/webots/scene_tree/WbAddNodeDialog.cpp | 4 +-- src/webots/scene_tree/WbSceneTree.cpp | 3 +- src/webots/user_commands/WbClipboard.cpp | 2 +- src/webots/vrml/WbField.cpp | 3 +- src/webots/vrml/WbFieldModel.cpp | 7 +++-- src/webots/vrml/WbFieldModel.hpp | 3 +- src/webots/vrml/WbFieldValueRestriction.cpp | 32 ++++++++++----------- src/webots/vrml/WbFieldValueRestriction.hpp | 6 ++-- 12 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/webots/nodes/utils/WbDictionary.cpp b/src/webots/nodes/utils/WbDictionary.cpp index 41950efb643..1f68312ea08 100644 --- a/src/webots/nodes/utils/WbDictionary.cpp +++ b/src/webots/nodes/utils/WbDictionary.cpp @@ -121,8 +121,8 @@ bool WbDictionary::updateDef(WbBaseNode *&node, WbSFNode *sfNode, WbMFNode *mfNo QString error; assert(node->parentField() && node->parentNode()); typeMatch = WbNodeUtilities::isAllowedToInsert(node->parentField(), definitionNode->nodeModelName(), node->parentNode(), - error, nodeUse, QString(), definitionNode->modelName(), definitionNode->model(), - definitionNode->proto()); + error, nodeUse, QString(), definitionNode->modelName(), + definitionNode->model(), definitionNode->proto()); match = typeMatch && !definitionNode->isAnAncestorOf(node); } @@ -138,10 +138,9 @@ bool WbDictionary::updateDef(WbBaseNode *&node, WbSFNode *sfNode, WbMFNode *mfNo definitionNode = static_cast(matchingNode->defNode()); QString error; assert(node->parentField() && node->parentNode()); - typeMatch = - WbNodeUtilities::isAllowedToInsert(node->parentField(), definitionNode->nodeModelName(), node->parentNode(), error, - nodeUse, QString(), definitionNode->modelName(), definitionNode->model(), - definitionNode->proto()); + typeMatch = WbNodeUtilities::isAllowedToInsert( + node->parentField(), definitionNode->nodeModelName(), node->parentNode(), error, nodeUse, QString(), + definitionNode->modelName(), definitionNode->model(), definitionNode->proto()); } } diff --git a/src/webots/nodes/utils/WbNodeUtilities.cpp b/src/webots/nodes/utils/WbNodeUtilities.cpp index 82bbbe0c832..f1cb46b75ff 100644 --- a/src/webots/nodes/utils/WbNodeUtilities.cpp +++ b/src/webots/nodes/utils/WbNodeUtilities.cpp @@ -580,18 +580,17 @@ namespace { return false; } - bool isSolidNode(WbBaseNode *node) { - return dynamic_cast(node); - } + bool isSolidNode(WbBaseNode *node) { return dynamic_cast(node); } - bool doesFieldRestrictionAcceptNode(const WbField *const field, const QString &nodeModelName, const WbNodeModel *nodeModel, const WbProtoModel *protoModel, const QStringList &protoParentList) { + bool doesFieldRestrictionAcceptNode(const WbField *const field, const QString &nodeModelName, const WbNodeModel *nodeModel, + const WbProtoModel *protoModel, const QStringList &protoParentList) { assert(field->hasRestrictedValues()); foreach (const WbFieldValueRestriction restriction, field->acceptedValues()) { - if(restriction.type() != WB_SF_NODE) + if (restriction.type() != WB_SF_NODE) continue; if (restriction.isProtoNodeTypeAccepted(protoModel) || restriction.isBaseNodeTypeAccepted(nodeModel) || (!protoModel && (restriction.toNode()->modelName() == nodeModelName || - (restriction.allowsSubtypes() && protoParentList.contains(restriction.toNode()->modelName()))))) + (restriction.allowsSubtypes() && protoParentList.contains(restriction.toNode()->modelName()))))) return true; } return false; @@ -1506,7 +1505,8 @@ bool WbNodeUtilities::isAllowedToInsert(const WbField *const field, const QStrin const QString &newNodeModel, const WbNodeModel *newNodeBaseModel, const WbProtoModel *newNodeProtoModel, const QStringList &newNodeProtoParentList, bool automaticBoundingObjectCheck) { - if (field->hasRestrictedValues() && !doesFieldRestrictionAcceptNode(field, newNodeModel, newNodeBaseModel, newNodeProtoModel, newNodeProtoParentList)) + if (field->hasRestrictedValues() && + !doesFieldRestrictionAcceptNode(field, newNodeModel, newNodeBaseModel, newNodeProtoModel, newNodeProtoParentList)) return false; if (field->isParameter()) { foreach (WbField *internalField, field->internalFields()) { @@ -1514,7 +1514,8 @@ bool WbNodeUtilities::isAllowedToInsert(const WbField *const field, const QStrin if (internalField->isParameter()) // recursive call: check only node field names and not parameter names valid = isAllowedToInsert(internalField, nodeName, internalField->parentNode(), errorMessage, WbNode::UNKNOWN_USE, type, - newNodeModel, newNodeBaseModel, newNodeProtoModel, newNodeProtoParentList, automaticBoundingObjectCheck); + newNodeModel, newNodeBaseModel, newNodeProtoModel, newNodeProtoParentList, + automaticBoundingObjectCheck); else { const WbNode *parentNode = internalField->parentNode(); valid = ::isAllowedToInsert(internalField->name(), nodeName, parentNode, errorMessage, diff --git a/src/webots/nodes/utils/WbNodeUtilities.hpp b/src/webots/nodes/utils/WbNodeUtilities.hpp index 27ca4f59b7e..903d51072ca 100644 --- a/src/webots/nodes/utils/WbNodeUtilities.hpp +++ b/src/webots/nodes/utils/WbNodeUtilities.hpp @@ -172,9 +172,9 @@ namespace WbNodeUtilities { // it first retrieve the base field and model and then check the validity // type is checked in case of Slot node bool isAllowedToInsert(const WbField *const field, const QString &nodeName, const WbNode *node, QString &errorMessage, - WbNode::NodeUse nodeUse, const QString &type, const QString &newNodeModel, const WbNodeModel *newNodeBaseModel, - const WbProtoModel *newNodeProtoModel, const QStringList &newNodeProtoParentList = QStringList(), - bool automaticBoundingObjectCheck = true); + WbNode::NodeUse nodeUse, const QString &type, const QString &newNodeModel, + const WbNodeModel *newNodeBaseModel, const WbProtoModel *newNodeProtoModel, + const QStringList &newNodeProtoParentList = QStringList(), bool automaticBoundingObjectCheck = true); // check existing node structure bool validateExistingChildNode(const WbField *const field, const WbNode *childNode, const WbNode *node, diff --git a/src/webots/nodes/utils/WbWorld.cpp b/src/webots/nodes/utils/WbWorld.cpp index 8612521c40a..ffb0c3fc79d 100644 --- a/src/webots/nodes/utils/WbWorld.cpp +++ b/src/webots/nodes/utils/WbWorld.cpp @@ -136,7 +136,8 @@ WbWorld::WbWorld(WbTokenizer *tokenizer) : } QString errorMessage; if (WbNodeUtilities::isAllowedToInsert(childrenField, node->nodeModelName(), mRoot, errorMessage, WbNode::STRUCTURE_USE, - WbNodeUtilities::slotType(node), node->modelName(), node->model(), node->proto())) { + WbNodeUtilities::slotType(node), node->modelName(), node->model(), + node->proto())) { node->validate(); mRoot->addChild(node); } else diff --git a/src/webots/scene_tree/WbAddNodeDialog.cpp b/src/webots/scene_tree/WbAddNodeDialog.cpp index 5d3218840f3..d58e4cb2645 100644 --- a/src/webots/scene_tree/WbAddNodeDialog.cpp +++ b/src/webots/scene_tree/WbAddNodeDialog.cpp @@ -558,8 +558,8 @@ int WbAddNodeDialog::addProtosFromProtoList(QTreeWidgetItem *parentItem, int typ QString errorMessage; const QString nodeName = it.key(); - if (!WbNodeUtilities::isAllowedToInsert(mField, baseType, mCurrentNode, errorMessage, nodeUse, info->slotType(), - nodeName, WbNodeModel::findModel(baseType), NULL, info->parents())) + if (!WbNodeUtilities::isAllowedToInsert(mField, baseType, mCurrentNode, errorMessage, nodeUse, info->slotType(), nodeName, + WbNodeModel::findModel(baseType), NULL, info->parents())) continue; // keep track of unique local proto that may clash diff --git a/src/webots/scene_tree/WbSceneTree.cpp b/src/webots/scene_tree/WbSceneTree.cpp index 08eb14bb660..0286ce9df90 100644 --- a/src/webots/scene_tree/WbSceneTree.cpp +++ b/src/webots/scene_tree/WbSceneTree.cpp @@ -1071,7 +1071,8 @@ bool WbSceneTree::isPasteAllowed() { QString errorMessage; if (!WbNodeUtilities::isAllowedToInsert(field, nodeModelName, parentNode, errorMessage, static_cast(parentNode)->nodeUse(), clipboardNodeInfo->slotType, - clipboardNodeInfo->modelName, WbNodeModel::findModel(nodeModelName), NULL, clipboardNodeInfo->protoParentList)) + clipboardNodeInfo->modelName, WbNodeModel::findModel(nodeModelName), NULL, + clipboardNodeInfo->protoParentList)) return false; if (clipboardNodeInfo->hasADeviceDescendant) diff --git a/src/webots/user_commands/WbClipboard.cpp b/src/webots/user_commands/WbClipboard.cpp index 529a2515733..d699efeb8ad 100644 --- a/src/webots/user_commands/WbClipboard.cpp +++ b/src/webots/user_commands/WbClipboard.cpp @@ -17,9 +17,9 @@ #include "WbBaseNode.hpp" #include "WbDictionary.hpp" #include "WbNodeUtilities.hpp" +#include "WbProtoModel.hpp" #include "WbRgb.hpp" #include "WbRotation.hpp" -#include "WbProtoModel.hpp" #include "WbVector2.hpp" #include "WbVector3.hpp" #include "WbVrmlNodeUtilities.hpp" diff --git a/src/webots/vrml/WbField.cpp b/src/webots/vrml/WbField.cpp index 29dd310a327..300b6f2b459 100644 --- a/src/webots/vrml/WbField.cpp +++ b/src/webots/vrml/WbField.cpp @@ -167,7 +167,8 @@ void WbField::checkValueIsAccepted() { if (!mModel->isValueAccepted(mValue, &refusedIndex)) { QString acceptedValuesList = ""; foreach (const WbFieldValueRestriction acceptedValue, mModel->acceptedValues()) - acceptedValuesList += acceptedValue.toSimplifiedStringRepresentation() + (acceptedValue.allowsSubtypes() ? "+" : "") + ", "; + acceptedValuesList += + acceptedValue.toSimplifiedStringRepresentation() + (acceptedValue.allowsSubtypes() ? "+" : "") + ", "; acceptedValuesList.chop(2); QString error; if (isSingle()) { diff --git a/src/webots/vrml/WbFieldModel.cpp b/src/webots/vrml/WbFieldModel.cpp index 1347297a9cc..efcf1a5def1 100644 --- a/src/webots/vrml/WbFieldModel.cpp +++ b/src/webots/vrml/WbFieldModel.cpp @@ -176,7 +176,8 @@ WbValue *WbFieldModel::createValueForVrmlType(const QString &type, WbTokenizer * return NULL; } -QList WbFieldModel::getAcceptedValues(const QString &type, WbTokenizer *tokenizer, const QString &worldPath) { +QList WbFieldModel::getAcceptedValues(const QString &type, WbTokenizer *tokenizer, + const QString &worldPath) { QList values; while (tokenizer->nextWord() != '}') { tokenizer->ungetToken(); @@ -186,7 +187,7 @@ QList WbFieldModel::getAcceptedValues(const QString &ty assert(singleValue); bool allowSubtypeMatch = tokenizer->peekWord() == '+'; - if(allowSubtypeMatch) + if (allowSubtypeMatch) tokenizer->nextToken(); WbFieldValueRestriction restriction(singleValue->variantValue(), allowSubtypeMatch); @@ -227,7 +228,7 @@ bool WbFieldModel::isValueAccepted(const WbValue *value, int *refusedIndex) cons } else { assert(singleValue); foreach (const WbFieldValueRestriction acceptedVariant, mAcceptedValues) { - if(acceptedVariant.isVariantAccepted(singleValue->variantValue())) + if (acceptedVariant.isVariantAccepted(singleValue->variantValue())) return true; } *refusedIndex = 0; diff --git a/src/webots/vrml/WbFieldModel.hpp b/src/webots/vrml/WbFieldModel.hpp index 24b838ac746..f2563acd94c 100644 --- a/src/webots/vrml/WbFieldModel.hpp +++ b/src/webots/vrml/WbFieldModel.hpp @@ -98,7 +98,8 @@ class WbFieldModel { mutable int mRefCount; static WbValue *createValueForVrmlType(const QString &type, WbTokenizer *tokenizer, const QString &worldPath); - static QList getAcceptedValues(const QString &type, WbTokenizer *tokenizer, const QString &worldPath); + static QList getAcceptedValues(const QString &type, WbTokenizer *tokenizer, + const QString &worldPath); }; #endif diff --git a/src/webots/vrml/WbFieldValueRestriction.cpp b/src/webots/vrml/WbFieldValueRestriction.cpp index 9d3cf68972d..fd5ceac6171 100644 --- a/src/webots/vrml/WbFieldValueRestriction.cpp +++ b/src/webots/vrml/WbFieldValueRestriction.cpp @@ -17,13 +17,13 @@ #include "WbNode.hpp" WbFieldValueRestriction &WbFieldValueRestriction::operator=(const WbFieldValueRestriction &other) { - WbVariant::operator =( other ); + WbVariant::operator=(other); mAllowsSubtypes = other.allowsSubtypes(); return *this; } bool WbFieldValueRestriction::operator==(const WbFieldValueRestriction &other) const { - return WbVariant::operator ==( other ) && allowsSubtypes() == other.allowsSubtypes(); + return WbVariant::operator==(other) && allowsSubtypes() == other.allowsSubtypes(); } bool WbFieldValueRestriction::operator!=(const WbFieldValueRestriction &other) const { @@ -31,22 +31,21 @@ bool WbFieldValueRestriction::operator!=(const WbFieldValueRestriction &other) c } bool WbFieldValueRestriction::isVariantAccepted(const WbVariant &variant) const { - if (type() != variant.type()) - return false; - if (type() != WB_SF_NODE) - return variant == *this; - return isNodeAccepted(variant.toNode()); + if (type() != variant.type()) + return false; + if (type() != WB_SF_NODE) + return variant == *this; + return isNodeAccepted(variant.toNode()); } bool WbFieldValueRestriction::isNodeAccepted(const WbNode *node) const { - if(type() != WB_SF_NODE) - return false; - if(!toNode() || !node) - return toNode() == node; - if (allowsSubtypes()) - return toNode()->isProtoInstance() ? isProtoNodeTypeAccepted(node->proto()) : - isBaseNodeTypeAccepted(node->model()); - return toNode()->modelName() == node->modelName() || toNode()->modelName() == node->nodeModelName(); + if (type() != WB_SF_NODE) + return false; + if (!toNode() || !node) + return toNode() == node; + if (allowsSubtypes()) + return toNode()->isProtoInstance() ? isProtoNodeTypeAccepted(node->proto()) : isBaseNodeTypeAccepted(node->model()); + return toNode()->modelName() == node->modelName() || toNode()->modelName() == node->nodeModelName(); } bool WbFieldValueRestriction::isBaseNodeTypeAccepted(const WbNodeModel *actualType) const { @@ -58,5 +57,6 @@ bool WbFieldValueRestriction::isBaseNodeTypeAccepted(const WbNodeModel *actualTy bool WbFieldValueRestriction::isProtoNodeTypeAccepted(const WbProtoModel *actualType) const { if (type() != WB_SF_NODE || !actualType) return false; - return toNode()->modelName() == actualType->name() || (allowsSubtypes() && isProtoNodeTypeAccepted(actualType->ancestorProtoModel())); + return toNode()->modelName() == actualType->name() || + (allowsSubtypes() && isProtoNodeTypeAccepted(actualType->ancestorProtoModel())); } diff --git a/src/webots/vrml/WbFieldValueRestriction.hpp b/src/webots/vrml/WbFieldValueRestriction.hpp index 8b5e636c4b1..75bf158fc60 100644 --- a/src/webots/vrml/WbFieldValueRestriction.hpp +++ b/src/webots/vrml/WbFieldValueRestriction.hpp @@ -22,7 +22,7 @@ #include "../../../include/controller/c/webots/supervisor.h" -class WbFieldValueRestriction: public WbVariant { +class WbFieldValueRestriction : public WbVariant { Q_OBJECT; public: @@ -36,7 +36,9 @@ class WbFieldValueRestriction: public WbVariant { explicit WbFieldValueRestriction(const WbRgb &c) : WbVariant(c), mAllowsSubtypes(false) {} explicit WbFieldValueRestriction(const WbRotation &r) : WbVariant(r), mAllowsSubtypes(false) {} explicit WbFieldValueRestriction(WbNode *n, bool allowsSubtypes) : WbVariant(n), mAllowsSubtypes(allowsSubtypes) {} - explicit WbFieldValueRestriction(const WbVariant &variant, bool allowsSubtypes) : WbVariant(variant), mAllowsSubtypes(allowsSubtypes && variant.type() == WB_SF_NODE) {} + explicit WbFieldValueRestriction(const WbVariant &variant, bool allowsSubtypes) : + WbVariant(variant), + mAllowsSubtypes(allowsSubtypes && variant.type() == WB_SF_NODE) {} WbFieldValueRestriction &operator=(const WbFieldValueRestriction &other); bool operator==(const WbFieldValueRestriction &other) const; bool operator!=(const WbFieldValueRestriction &other) const; From 13234e165086c30577e14c5d03de77dce6946c98 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Mon, 8 Jul 2024 02:23:32 -0700 Subject: [PATCH 32/37] improve node insertion validity api --- src/webots/nodes/utils/WbDictionary.cpp | 19 +++++-------- src/webots/nodes/utils/WbNodeOperations.cpp | 5 ++-- src/webots/nodes/utils/WbNodeUtilities.cpp | 30 +++++++++------------ src/webots/nodes/utils/WbNodeUtilities.hpp | 24 +++++++++++++---- src/webots/nodes/utils/WbWorld.cpp | 5 ++-- src/webots/scene_tree/WbAddNodeDialog.cpp | 8 +++--- src/webots/scene_tree/WbSceneTree.cpp | 5 ++-- src/webots/vrml/WbFieldValueRestriction.cpp | 16 +++++++---- src/webots/vrml/WbFieldValueRestriction.hpp | 6 +++-- 9 files changed, 63 insertions(+), 55 deletions(-) diff --git a/src/webots/nodes/utils/WbDictionary.cpp b/src/webots/nodes/utils/WbDictionary.cpp index 1f68312ea08..d77fb3a84c0 100644 --- a/src/webots/nodes/utils/WbDictionary.cpp +++ b/src/webots/nodes/utils/WbDictionary.cpp @@ -120,9 +120,8 @@ bool WbDictionary::updateDef(WbBaseNode *&node, WbSFNode *sfNode, WbMFNode *mfNo definitionNode = static_cast(defNodes[defIndex]); QString error; assert(node->parentField() && node->parentNode()); - typeMatch = WbNodeUtilities::isAllowedToInsert(node->parentField(), definitionNode->nodeModelName(), node->parentNode(), - error, nodeUse, QString(), definitionNode->modelName(), - definitionNode->model(), definitionNode->proto()); + typeMatch = WbNodeUtilities::isAllowedToInsert(node->parentField(), node->parentNode(), error, nodeUse, QString(), + definitionNode); match = typeMatch && !definitionNode->isAnAncestorOf(node); } @@ -138,9 +137,8 @@ bool WbDictionary::updateDef(WbBaseNode *&node, WbSFNode *sfNode, WbMFNode *mfNo definitionNode = static_cast(matchingNode->defNode()); QString error; assert(node->parentField() && node->parentNode()); - typeMatch = WbNodeUtilities::isAllowedToInsert( - node->parentField(), definitionNode->nodeModelName(), node->parentNode(), error, nodeUse, QString(), - definitionNode->modelName(), definitionNode->model(), definitionNode->proto()); + typeMatch = WbNodeUtilities::isAllowedToInsert(node->parentField(), node->parentNode(), error, nodeUse, QString(), + definitionNode); } } @@ -465,8 +463,7 @@ bool WbDictionary::checkBoundingObjectConstraints(const WbBaseNode *defNode, QSt if (sfnode) { const WbNode *const n = sfnode->value(); if (n) { - if (!WbNodeUtilities::isAllowedToInsert(fields[i], n->nodeModelName(), parentNode, errorMessage, nodeUse, QString(), - n->modelName(), n->model(), n->proto(), QStringList(), false)) + if (!WbNodeUtilities::isAllowedToInsert(fields[i], parentNode, errorMessage, nodeUse, QString(), n, false)) return false; subNodes << n; } @@ -477,8 +474,7 @@ bool WbDictionary::checkBoundingObjectConstraints(const WbBaseNode *defNode, QSt for (int j = 0; j < size; ++j) { const WbNode *const n = mfnode->item(j); if (n) { - if (!WbNodeUtilities::isAllowedToInsert(fields[i], n->nodeModelName(), parentNode, errorMessage, nodeUse, - QString(), n->modelName(), n->model(), n->proto(), QStringList(), false)) + if (!WbNodeUtilities::isAllowedToInsert(fields[i], parentNode, errorMessage, nodeUse, QString(), n, false)) return false; subNodes << n; @@ -536,8 +532,7 @@ bool WbDictionary::isSuitable(const WbNode *defNode, const QString &type) const assert(mTargetField); QString errorMessage; const WbNode::NodeUse targetNodeUse = static_cast(mTargetNode)->nodeUse(); - if (!WbNodeUtilities::isAllowedToInsert(mTargetField, defNode->nodeModelName(), mTargetNode, errorMessage, targetNodeUse, - type, defNode->modelName(), defNode->model(), defNode->proto(), QStringList(), true)) + if (!WbNodeUtilities::isAllowedToInsert(mTargetField, mTargetNode, errorMessage, targetNodeUse, type, defNode, true)) return false; const WbBaseNode *defBaseNode = dynamic_cast(defNode); diff --git a/src/webots/nodes/utils/WbNodeOperations.cpp b/src/webots/nodes/utils/WbNodeOperations.cpp index 10aa38649b2..4d20d1856fa 100644 --- a/src/webots/nodes/utils/WbNodeOperations.cpp +++ b/src/webots/nodes/utils/WbNodeOperations.cpp @@ -182,9 +182,8 @@ WbNodeOperations::OperationResult WbNodeOperations::importNode(WbNode *parentNod foreach (WbNode *node, nodes) { childNode = static_cast(node); QString errorMessage; - if (WbNodeUtilities::isAllowedToInsert(field, childNode->nodeModelName(), parentNode, errorMessage, nodeUse, - WbNodeUtilities::slotType(childNode), childNode->modelName(), childNode->model(), - childNode->proto(), QStringList(), false)) { + if (WbNodeUtilities::isAllowedToInsert(field, parentNode, errorMessage, nodeUse, WbNodeUtilities::slotType(childNode), + childNode, false)) { if (avoidIntersections) tryToAvoidIntersections(childNode); const OperationResult result = initNewNode(childNode, parentNode, field, nodeIndex, true); diff --git a/src/webots/nodes/utils/WbNodeUtilities.cpp b/src/webots/nodes/utils/WbNodeUtilities.cpp index f1cb46b75ff..a8149dd6cd3 100644 --- a/src/webots/nodes/utils/WbNodeUtilities.cpp +++ b/src/webots/nodes/utils/WbNodeUtilities.cpp @@ -583,16 +583,11 @@ namespace { bool isSolidNode(WbBaseNode *node) { return dynamic_cast(node); } bool doesFieldRestrictionAcceptNode(const WbField *const field, const QString &nodeModelName, const WbNodeModel *nodeModel, - const WbProtoModel *protoModel, const QStringList &protoParentList) { + const QStringList &protoParentList) { assert(field->hasRestrictedValues()); - foreach (const WbFieldValueRestriction restriction, field->acceptedValues()) { - if (restriction.type() != WB_SF_NODE) - continue; - if (restriction.isProtoNodeTypeAccepted(protoModel) || restriction.isBaseNodeTypeAccepted(nodeModel) || - (!protoModel && (restriction.toNode()->modelName() == nodeModelName || - (restriction.allowsSubtypes() && protoParentList.contains(restriction.toNode()->modelName()))))) + foreach (const WbFieldValueRestriction restriction, field->acceptedValues()) + if (restriction.isNodeAccepted(nodeModelName, nodeModel, protoParentList)) return true; - } return false; } }; // namespace @@ -1500,25 +1495,23 @@ bool WbNodeUtilities::validateExistingChildNode(const WbField *const field, cons WbNodeUtilities::slotType(childNode)); } -bool WbNodeUtilities::isAllowedToInsert(const WbField *const field, const QString &nodeName, const WbNode *node, - QString &errorMessage, WbNode::NodeUse nodeUse, const QString &type, - const QString &newNodeModel, const WbNodeModel *newNodeBaseModel, - const WbProtoModel *newNodeProtoModel, const QStringList &newNodeProtoParentList, +bool WbNodeUtilities::isAllowedToInsert(const WbField *const field, const WbNode *node, QString &errorMessage, + WbNode::NodeUse nodeUse, const QString &type, const QString &newNodeModelName, + const WbNodeModel *newNodeBaseModel, const QStringList &newNodeProtoParentList, bool automaticBoundingObjectCheck) { if (field->hasRestrictedValues() && - !doesFieldRestrictionAcceptNode(field, newNodeModel, newNodeBaseModel, newNodeProtoModel, newNodeProtoParentList)) + !doesFieldRestrictionAcceptNode(field, newNodeModelName, newNodeBaseModel, newNodeProtoParentList)) return false; if (field->isParameter()) { foreach (WbField *internalField, field->internalFields()) { bool valid; if (internalField->isParameter()) // recursive call: check only node field names and not parameter names - valid = isAllowedToInsert(internalField, nodeName, internalField->parentNode(), errorMessage, WbNode::UNKNOWN_USE, type, - newNodeModel, newNodeBaseModel, newNodeProtoModel, newNodeProtoParentList, - automaticBoundingObjectCheck); + valid = isAllowedToInsert(internalField, internalField->parentNode(), errorMessage, WbNode::UNKNOWN_USE, type, + newNodeModelName, newNodeBaseModel, newNodeProtoParentList, automaticBoundingObjectCheck); else { const WbNode *parentNode = internalField->parentNode(); - valid = ::isAllowedToInsert(internalField->name(), nodeName, parentNode, errorMessage, + valid = ::isAllowedToInsert(internalField->name(), newNodeBaseModel->name(), parentNode, errorMessage, static_cast(parentNode)->nodeUse(), type, automaticBoundingObjectCheck); } if (!valid) @@ -1526,7 +1519,8 @@ bool WbNodeUtilities::isAllowedToInsert(const WbField *const field, const QStrin } return true; } else - return ::isAllowedToInsert(field->name(), nodeName, node, errorMessage, nodeUse, type, automaticBoundingObjectCheck); + return ::isAllowedToInsert(field->name(), newNodeBaseModel->name(), node, errorMessage, nodeUse, type, + automaticBoundingObjectCheck); } WbNodeUtilities::Answer WbNodeUtilities::isSuitableForTransform(const WbNode *const srcNode, const QString &destModelName, diff --git a/src/webots/nodes/utils/WbNodeUtilities.hpp b/src/webots/nodes/utils/WbNodeUtilities.hpp index 903d51072ca..1d9994785af 100644 --- a/src/webots/nodes/utils/WbNodeUtilities.hpp +++ b/src/webots/nodes/utils/WbNodeUtilities.hpp @@ -22,7 +22,9 @@ // #include "WbNode.hpp" +#include "WbNodeModel.hpp" #include "WbOdeTypes.hpp" +#include "WbProtoModel.hpp" #include @@ -167,14 +169,26 @@ namespace WbNodeUtilities { // return false if the Slot structure is invalid and insertion should be aborted bool validateInsertedNode(WbField *field, const WbNode *newNode, const WbNode *parentNode, bool isInBoundingObject); - // check if a node with node model 'modelName' can be inserted in the field 'field' of parent node 'node' + // check if a new node with the given parameters can be inserted in the field 'field' of parent node 'node' // in case of PROTO parent node and parameter field, // it first retrieve the base field and model and then check the validity // type is checked in case of Slot node - bool isAllowedToInsert(const WbField *const field, const QString &nodeName, const WbNode *node, QString &errorMessage, - WbNode::NodeUse nodeUse, const QString &type, const QString &newNodeModel, - const WbNodeModel *newNodeBaseModel, const WbProtoModel *newNodeProtoModel, - const QStringList &newNodeProtoParentList = QStringList(), bool automaticBoundingObjectCheck = true); + bool isAllowedToInsert(const WbField *const field, const WbNode *node, QString &errorMessage, WbNode::NodeUse nodeUse, + const QString &type, const QString &newNodeModelName, const WbNodeModel *newNodeBaseModel, + const QStringList &newNodeProtoParentList, bool automaticBoundingObjectCheck = true); + inline bool isAllowedToInsert(const WbField *const field, const WbNode *node, QString &errorMessage, WbNode::NodeUse nodeUse, + const QString &type, const QString &newNodeBaseModelName, const QString &newNodeModelName, + const QStringList &newNodeProtoParentList, bool automaticBoundingObjectCheck = true) { + return isAllowedToInsert(field, node, errorMessage, nodeUse, type, newNodeModelName, + WbNodeModel::findModel(newNodeBaseModelName), newNodeProtoParentList, + automaticBoundingObjectCheck); + } + inline bool isAllowedToInsert(const WbField *const field, const WbNode *node, QString &errorMessage, WbNode::NodeUse nodeUse, + const QString &type, const WbNode *newNode, bool automaticBoundingObjectCheck = true) { + return isAllowedToInsert(field, node, errorMessage, nodeUse, type, node->modelName(), node->model(), + node->isProtoInstance() ? node->proto()->parentProtoNames() : QStringList(), + automaticBoundingObjectCheck); + } // check existing node structure bool validateExistingChildNode(const WbField *const field, const WbNode *childNode, const WbNode *node, diff --git a/src/webots/nodes/utils/WbWorld.cpp b/src/webots/nodes/utils/WbWorld.cpp index ffb0c3fc79d..1864c0d33a1 100644 --- a/src/webots/nodes/utils/WbWorld.cpp +++ b/src/webots/nodes/utils/WbWorld.cpp @@ -135,9 +135,8 @@ WbWorld::WbWorld(WbTokenizer *tokenizer) : return; } QString errorMessage; - if (WbNodeUtilities::isAllowedToInsert(childrenField, node->nodeModelName(), mRoot, errorMessage, WbNode::STRUCTURE_USE, - WbNodeUtilities::slotType(node), node->modelName(), node->model(), - node->proto())) { + if (WbNodeUtilities::isAllowedToInsert(childrenField, mRoot, errorMessage, WbNode::STRUCTURE_USE, + WbNodeUtilities::slotType(node), node)) { node->validate(); mRoot->addChild(node); } else diff --git a/src/webots/scene_tree/WbAddNodeDialog.cpp b/src/webots/scene_tree/WbAddNodeDialog.cpp index d58e4cb2645..1855224a3f8 100644 --- a/src/webots/scene_tree/WbAddNodeDialog.cpp +++ b/src/webots/scene_tree/WbAddNodeDialog.cpp @@ -435,8 +435,8 @@ void WbAddNodeDialog::buildTree() { QFileInfo fileInfo(basicNodeName); QString errorMessage; if (fileInfo.baseName().contains(regexp) && - WbNodeUtilities::isAllowedToInsert(mField, fileInfo.baseName(), mCurrentNode, errorMessage, nodeUse, QString(), - fileInfo.baseName(), WbNodeModel::findModel(fileInfo.baseName()), NULL)) { + WbNodeUtilities::isAllowedToInsert(mField, mCurrentNode, errorMessage, nodeUse, QString(), fileInfo.baseName(), + fileInfo.baseName(), QStringList())) { item = new QTreeWidgetItem(nodesItem, QStringList(fileInfo.baseName())); item->setIcon(0, QIcon("enabledIcons:node.png")); nodesItem->addChild(item); @@ -558,8 +558,8 @@ int WbAddNodeDialog::addProtosFromProtoList(QTreeWidgetItem *parentItem, int typ QString errorMessage; const QString nodeName = it.key(); - if (!WbNodeUtilities::isAllowedToInsert(mField, baseType, mCurrentNode, errorMessage, nodeUse, info->slotType(), nodeName, - WbNodeModel::findModel(baseType), NULL, info->parents())) + if (!WbNodeUtilities::isAllowedToInsert(mField, mCurrentNode, errorMessage, nodeUse, info->slotType(), baseType, nodeName, + info->parents())) continue; // keep track of unique local proto that may clash diff --git a/src/webots/scene_tree/WbSceneTree.cpp b/src/webots/scene_tree/WbSceneTree.cpp index 0286ce9df90..7e719c39013 100644 --- a/src/webots/scene_tree/WbSceneTree.cpp +++ b/src/webots/scene_tree/WbSceneTree.cpp @@ -1069,10 +1069,9 @@ bool WbSceneTree::isPasteAllowed() { const WbClipboard::WbClipboardNodeInfo *clipboardNodeInfo = mClipboard->nodeInfo(); const QString &nodeModelName = clipboardNodeInfo->nodeModelName; QString errorMessage; - if (!WbNodeUtilities::isAllowedToInsert(field, nodeModelName, parentNode, errorMessage, + if (!WbNodeUtilities::isAllowedToInsert(field, parentNode, errorMessage, static_cast(parentNode)->nodeUse(), clipboardNodeInfo->slotType, - clipboardNodeInfo->modelName, WbNodeModel::findModel(nodeModelName), NULL, - clipboardNodeInfo->protoParentList)) + nodeModelName, clipboardNodeInfo->modelName, clipboardNodeInfo->protoParentList)) return false; if (clipboardNodeInfo->hasADeviceDescendant) diff --git a/src/webots/vrml/WbFieldValueRestriction.cpp b/src/webots/vrml/WbFieldValueRestriction.cpp index fd5ceac6171..b08636634f0 100644 --- a/src/webots/vrml/WbFieldValueRestriction.cpp +++ b/src/webots/vrml/WbFieldValueRestriction.cpp @@ -38,24 +38,30 @@ bool WbFieldValueRestriction::isVariantAccepted(const WbVariant &variant) const return isNodeAccepted(variant.toNode()); } +bool WbFieldValueRestriction::isNodeAccepted(const QString &nodeModelName, const WbNodeModel *nodeModel, + const QStringList &protoParentList) const { + if (type() != WB_SF_NODE || !toNode() || !nodeModel) + return false; + return nodeModelName == toNode()->modelName() || isBaseNodeTypeAccepted(nodeModel) || + (allowsSubtypes() && protoParentList.contains(toNode()->modelName())); +} + bool WbFieldValueRestriction::isNodeAccepted(const WbNode *node) const { if (type() != WB_SF_NODE) return false; if (!toNode() || !node) return toNode() == node; - if (allowsSubtypes()) - return toNode()->isProtoInstance() ? isProtoNodeTypeAccepted(node->proto()) : isBaseNodeTypeAccepted(node->model()); - return toNode()->modelName() == node->modelName() || toNode()->modelName() == node->nodeModelName(); + return toNode()->isProtoInstance() ? isProtoNodeTypeAccepted(node->proto()) : isBaseNodeTypeAccepted(node->model()); } bool WbFieldValueRestriction::isBaseNodeTypeAccepted(const WbNodeModel *actualType) const { - if (type() != WB_SF_NODE || !actualType) + if (type() != WB_SF_NODE || !toNode() || !actualType) return false; return toNode()->modelName() == actualType->name() || (allowsSubtypes() && isBaseNodeTypeAccepted(actualType->parentModel())); } bool WbFieldValueRestriction::isProtoNodeTypeAccepted(const WbProtoModel *actualType) const { - if (type() != WB_SF_NODE || !actualType) + if (type() != WB_SF_NODE || !toNode() || !actualType) return false; return toNode()->modelName() == actualType->name() || (allowsSubtypes() && isProtoNodeTypeAccepted(actualType->ancestorProtoModel())); diff --git a/src/webots/vrml/WbFieldValueRestriction.hpp b/src/webots/vrml/WbFieldValueRestriction.hpp index 75bf158fc60..9c952201ce7 100644 --- a/src/webots/vrml/WbFieldValueRestriction.hpp +++ b/src/webots/vrml/WbFieldValueRestriction.hpp @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef WB_FIELD_VALUE_RESTRICTION -#define WB_FIELD_VALUE_RESTRICTION +#ifndef WB_FIELD_VALUE_RESTRICTION_HPP +#define WB_FIELD_VALUE_RESTRICTION_HPP #include #include @@ -48,8 +48,10 @@ class WbFieldValueRestriction : public WbVariant { const bool allowsSubtypes() const { return mAllowsSubtypes; } bool isVariantAccepted(const WbVariant &node) const; + bool isNodeAccepted(const QString &nodeModelName, const WbNodeModel *nodeModel, const QStringList &protoParentList) const; bool isNodeAccepted(const WbNode *node) const; bool isBaseNodeTypeAccepted(const WbNodeModel *actualType) const; + // Note: This does not check if the proto's base type would be accepted by isBaseNodeTypeAccepted bool isProtoNodeTypeAccepted(const WbProtoModel *actualType) const; private: From 698a1110cf7021596eac1a5c0fde9749d5b1f968 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:16:54 -0700 Subject: [PATCH 33/37] bug fixes --- src/webots/vrml/WbFieldValueRestriction.hpp | 2 +- .../protos/ProtoRestrictedFieldValues.proto | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/webots/vrml/WbFieldValueRestriction.hpp b/src/webots/vrml/WbFieldValueRestriction.hpp index 9c952201ce7..c9b3cef017d 100644 --- a/src/webots/vrml/WbFieldValueRestriction.hpp +++ b/src/webots/vrml/WbFieldValueRestriction.hpp @@ -47,7 +47,7 @@ class WbFieldValueRestriction : public WbVariant { const bool allowsSubtypes() const { return mAllowsSubtypes; } - bool isVariantAccepted(const WbVariant &node) const; + bool isVariantAccepted(const WbVariant &variant) const; bool isNodeAccepted(const QString &nodeModelName, const WbNodeModel *nodeModel, const QStringList &protoParentList) const; bool isNodeAccepted(const WbNode *node) const; bool isBaseNodeTypeAccepted(const WbNodeModel *actualType) const; diff --git a/tests/parser/protos/ProtoRestrictedFieldValues.proto b/tests/parser/protos/ProtoRestrictedFieldValues.proto index d30513d8985..08bc8af0836 100644 --- a/tests/parser/protos/ProtoRestrictedFieldValues.proto +++ b/tests/parser/protos/ProtoRestrictedFieldValues.proto @@ -7,13 +7,23 @@ PROTO ProtoRestrictedFieldValues field SFRotation{0 1 0 0, 0 0 1 0, 0 1 0 1.5708} rotation 0 0 1 0 field SFVec3f{0 0 0} translation 0 0 0 field MFNode{Solid{}+, PoseProto{}+} extensionSlot [] - unconnectedField MFNode{PoseProto{}} extensionSlot2 [] - unconnectedField MFNode{Pose{}} extensionSlot3 [] + field MFNode{PoseProto{}} extensionSlot2 [] + field MFNode{Pose{}} extensionSlot3 [] ] { Solid { rotation IS rotation translation IS translation - children IS extensionSlot + children [ + Group { + children IS extensionSlot + } + Group { + children IS extensionSlot2 + } + Group { + children IS extensionSlot3 + } + ] } } From 16f6a783517ad341eeb0bac69ceaac7b74608d21 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:42:31 -0700 Subject: [PATCH 34/37] fix file formatting --- src/webots/nodes/utils/WbNodeUtilities.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/webots/nodes/utils/WbNodeUtilities.cpp b/src/webots/nodes/utils/WbNodeUtilities.cpp index a8149dd6cd3..7efcfc78847 100644 --- a/src/webots/nodes/utils/WbNodeUtilities.cpp +++ b/src/webots/nodes/utils/WbNodeUtilities.cpp @@ -580,7 +580,9 @@ namespace { return false; } - bool isSolidNode(WbBaseNode *node) { return dynamic_cast(node); } + bool isSolidNode(WbBaseNode *node) { + return dynamic_cast(node); + } bool doesFieldRestrictionAcceptNode(const WbField *const field, const QString &nodeModelName, const WbNodeModel *nodeModel, const QStringList &protoParentList) { From f3790c0aca6aa595409f8a54b3bb34ae236c05d5 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Mon, 8 Jul 2024 13:54:05 -0700 Subject: [PATCH 35/37] additional documentation --- docs/reference/proto-definition.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/reference/proto-definition.md b/docs/reference/proto-definition.md index 44e3b3b858d..8535b3c4d02 100644 --- a/docs/reference/proto-definition.md +++ b/docs/reference/proto-definition.md @@ -83,7 +83,9 @@ PROTO MyProto [ For `SFNode`/`MFNode` fields, the `{}+` syntax allows the field to also accept nodes which derive from a specific node type. -In this example, the `color` field value can only be `0 0 0`, `0.5 0.5 0.5` or `1 1 1` and the `extensionSlot` field can only accept [Pose](pose.md) nodes, PROTOs whose base type is [Pose](pose.md), [Solid](solid.md) nodes, nodes derived from [Solid](solid.md), and PROTOs derived from [Solid](solid.md) or a type that inherits from [Solid](../reference/solid.md). Note that because `Pose{}` is not followed by a `+`, `extensionSlot` does not accept nodes that derive from [Pose](pose.md) (e.g. [Transform](transform.md) or [Fluid](fluid.md)) or PROTOs whose base type is not [Pose](pose.md). +In this example, the `color` field value can only be `0 0 0`, `0.5 0.5 0.5` or `1 1 1`, and the `extensionSlot` field can only accept [Pose](pose.md) nodes, PROTOs whose base type is [Pose](pose.md), [Solid](solid.md) nodes, nodes derived from [Solid](solid.md), and PROTOs derived from [Solid](solid.md) or a type that inherits from [Solid](../reference/solid.md). Note that because `Pose{}` is not followed by a `+`, `extensionSlot` does not accept nodes that derive from [Pose](pose.md) (e.g. [Transform](transform.md) or [Fluid](fluid.md)) or PROTOs whose base type is not [Pose](pose.md). + +Because [Solid](solid.md) derives from [Pose](pose.md), we could have allowed all the same node types by using `MFNode{Pose{}+}`, but this would also allow other descendants of [Pose](pose.md) such as [Transform](transform.md) or [Fluid](fluid.md). ### IS Statements From 6cae940d4d9ade4bfe50a13b55e63ab2ba50d835 Mon Sep 17 00:00:00 2001 From: CoolSpy3 Date: Mon, 8 Jul 2024 22:13:26 -0700 Subject: [PATCH 36/37] fix isAllowedToInsert --- src/webots/nodes/utils/WbNodeUtilities.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webots/nodes/utils/WbNodeUtilities.hpp b/src/webots/nodes/utils/WbNodeUtilities.hpp index 1d9994785af..8acf21452bc 100644 --- a/src/webots/nodes/utils/WbNodeUtilities.hpp +++ b/src/webots/nodes/utils/WbNodeUtilities.hpp @@ -185,8 +185,8 @@ namespace WbNodeUtilities { } inline bool isAllowedToInsert(const WbField *const field, const WbNode *node, QString &errorMessage, WbNode::NodeUse nodeUse, const QString &type, const WbNode *newNode, bool automaticBoundingObjectCheck = true) { - return isAllowedToInsert(field, node, errorMessage, nodeUse, type, node->modelName(), node->model(), - node->isProtoInstance() ? node->proto()->parentProtoNames() : QStringList(), + return isAllowedToInsert(field, node, errorMessage, nodeUse, type, newNode->modelName(), newNode->model(), + newNode->isProtoInstance() ? newNode->proto()->parentProtoNames() : QStringList(), automaticBoundingObjectCheck); } From 7792d534ac18997b76abd302edc0c7211a8fe428 Mon Sep 17 00:00:00 2001 From: CoolSpy3 <55305038+CoolSpy3@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:01:41 -0700 Subject: [PATCH 37/37] address comments from PR Co-authored-by: Olivier Michel --- src/webots/vrml/WbProtoModel.cpp | 3 +-- tests/manual_tests/protos/DerivedPoseProto.proto | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/webots/vrml/WbProtoModel.cpp b/src/webots/vrml/WbProtoModel.cpp index 8d5e3f98ebc..ae3e81dad0e 100644 --- a/src/webots/vrml/WbProtoModel.cpp +++ b/src/webots/vrml/WbProtoModel.cpp @@ -420,9 +420,8 @@ WbNode *WbProtoModel::generateRoot(const QVector ¶meters, const Q QStringList WbProtoModel::parentProtoNames() const { QStringList parents; const WbProtoModel *parentProtoModel = this; - while ((parentProtoModel = parentProtoModel->ancestorProtoModel())) { + while ((parentProtoModel = parentProtoModel->ancestorProtoModel())) parents << parentProtoModel->name(); - } return parents; } diff --git a/tests/manual_tests/protos/DerivedPoseProto.proto b/tests/manual_tests/protos/DerivedPoseProto.proto index 6e71e943871..7d95678e36f 100644 --- a/tests/manual_tests/protos/DerivedPoseProto.proto +++ b/tests/manual_tests/protos/DerivedPoseProto.proto @@ -8,7 +8,7 @@ PROTO DerivedPoseProto [ ] { PoseProto { - translation IS translation - rotation IS rotation + translation IS translation + rotation IS rotation } }