# SPDX-License-Identifier:  MPL-2.0
# Author: Minh Nguyen (minh@mail.minhnh.com)
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix qudt: <http://qudt.org/schema/qudt/> .
@prefix geom: <https://comp-rob2b.github.io/metamodels/geometry/structural-entities#> .
@prefix geom-coord: <https://comp-rob2b.github.io/metamodels/geometry/coordinates#> .
@prefix geom-rel: <https://comp-rob2b.github.io/metamodels/geometry/spatial-relations#> .

# **********
# Relation constraints
geom-rel:PositionShape
  a sh:NodeShape ;
  sh:targetClass geom-rel:Position ;
  sh:message "A position must relate exactly one point to exactly one reference point." ;
  sh:property [
    sh:path geom-rel:of ;
    sh:class geom:Point ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path geom-rel:with-respect-to ;
    sh:class geom:Point ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .
geom-rel:OrientationShape
  a sh:NodeShape ;
  sh:targetClass geom-rel:Orientation ;
  sh:message "An orientation must relate exactly one frame to exactly one reference frame." ;
  sh:property [
    sh:path geom-rel:of ;
    sh:class geom:Frame ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path geom-rel:with-respect-to ;
    sh:class geom:Frame ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .
geom-rel:PoseShape
  a sh:NodeShape ;
  sh:targetClass geom-rel:Pose ;
  sh:message "A pose must relate exactly one frame to exactly one reference frame." ;
  sh:property [
    sh:path geom-rel:of ;
    sh:class geom:Frame ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path geom-rel:with-respect-to ;
    sh:class geom:Frame ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

# **********
# Coordinates constraints
geom-coord:PoseDifferenceCoordinate
  a sh:NodeShape ;
  sh:targetClass geom-coord:PoseDifferenceCoordinate ;
  sh:message "A pose-difference coordinate needs one linear vector, one angular vector, and the frame in which it is expressed." ;
  sh:property [
    sh:path geom-coord:linear ;
    sh:class geom-coord:VectorXYZ ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path geom-coord:angular ;
    sh:class geom-coord:VectorXYZ ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property geom-coord:AsSeenByShape .

geom-coord:AsSeenByShape
  a sh:PropertyShape ;
  sh:path geom-coord:as-seen-by ;
  sh:class geom:Frame ;
  sh:minCount 1 ;
  sh:maxCount 1 .

geom-coord:PositionReferenceShape
  a sh:NodeShape ;
  sh:targetClass geom-coord:PositionReference ;
  sh:message "A position reference must identify exactly one position relation." ;
  sh:property [
    sh:path geom-coord:of-position ;
    sh:class geom-rel:Position ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

geom-coord:PositionCoordinateShape
  a sh:NodeShape ;
  sh:targetClass geom-coord:PositionCoordinate ;
  sh:message "A position coordinate must state the frame in which it is expressed." ;
  sh:property geom-coord:AsSeenByShape .

geom-coord:PoseReferenceShape
  a sh:NodeShape ;
  sh:targetClass geom-coord:PoseReference ;
  sh:message "A pose reference must identify exactly one pose relation." ;
  sh:property [
    sh:path geom-coord:of-pose ;
    sh:class geom-rel:Pose ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

geom-coord:PoseCoordinateShape
  a sh:NodeShape ;
  sh:targetClass geom-coord:PoseCoordinate ;
  sh:message "A pose coordinate must state the frame in which it is expressed." ;
  sh:property geom-coord:AsSeenByShape .

geom-coord:EulerAnglesShape
  a sh:NodeShape ;
  sh:targetClass geom-coord:EulerAngles ;
  sh:message "Euler angles require exactly one supported axes sequence." ;
  sh:property [
    sh:path geom-coord:axes-sequence ;
    sh:datatype xsd:string ;
    sh:pattern "^(xyz|xzy|yxz|yzx|zxy|zyx|xyx|xzx|yxy|yzy|zxz|zyz)$";
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:xone (
    [ sh:property [ sh:path rdf:type ; sh:hasValue geom-coord:Intrinsic ] ]
    [ sh:property [ sh:path rdf:type ; sh:hasValue geom-coord:Extrinsic ] ]
  ) .

geom-coord:UnitQuaternionConstraint
  a sh:SPARQLConstraint ;
  sh:message "A quaternion used as an orientation must have unit length." ;
  sh:select """
    PREFIX geom-coord: <https://comp-rob2b.github.io/metamodels/geometry/coordinates#>
    SELECT $this
    WHERE {
      $this geom-coord:x ?x ; geom-coord:y ?y ; geom-coord:z ?z ; geom-coord:w ?w .
      BIND((?x * ?x + ?y * ?y + ?z * ?z + ?w * ?w) AS ?normSquared)
      FILTER(ABS(?normSquared - 1.0) > 0.000002)
    }
  """ .

geom-coord:QuaternionShape
  a sh:NodeShape ;
  sh:targetClass geom-coord:Quaternion ;
  sh:message "A quaternion requires exactly one double-valued x, y, z and w component." ;
  sh:property [ sh:path geom-coord:x ; sh:datatype xsd:double ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path geom-coord:y ; sh:datatype xsd:double ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path geom-coord:z ; sh:datatype xsd:double ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path geom-coord:w ; sh:datatype xsd:double ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:sparql geom-coord:UnitQuaternionConstraint .

geom-coord:AnglesAlphaBetaGammaShape
  a sh:NodeShape ;
  sh:targetClass geom-coord:AnglesAlphaBetaGamma ;
  sh:message "Alpha-beta-gamma angles require a unit and exactly one double-valued alpha, beta, and gamma." ;
  sh:property [
    sh:path qudt:unit ;
    sh:minCount 1 ;
  ] ;
  sh:property [
    sh:path geom-coord:alpha ;
    sh:datatype xsd:double ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path geom-coord:beta ;
    sh:datatype xsd:double ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path geom-coord:gamma ;
    sh:datatype xsd:double ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

geom-coord:HasCoordinatePolicyShape
  a sh:NodeShape ;
  sh:targetSubjectsOf geom-coord:has-coordinate-policy ;
  sh:message "A relation names at most one coordinate-selection policy, by IRI." ;
  sh:property [
    sh:path geom-coord:has-coordinate-policy ;
    sh:nodeKind sh:IRI ;
    sh:maxCount 1 ;
  ] .
