From cb9ddc4e2fdf3a23f6d5355411df8b825a6bcff6 Mon Sep 17 00:00:00 2001 From: tomcrane Date: Wed, 10 Dec 2025 13:01:12 -0500 Subject: [PATCH 1/6] model text for activating --- source/presentation/4.0/index.md | 15 ++++-- source/presentation/4.0/model.md | 75 ++++++++++++++++++++++++------ source/presentation/4.0/scratch.md | 27 +++++++++++ 3 files changed, 99 insertions(+), 18 deletions(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index 2d7a29853..39523d7d9 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -2004,7 +2004,9 @@ The resource the user should be taken to is the `body` of the annotation, and th Sometimes it is necessary to modify the contents of a Container in the contexts of different annotations on that Container. This technique allows IIIF to be used for exhibitions, storytelling (fwd ref) and other interactive applications beyond simply conveying a set of static resources in a Container. -Annotations with the motivation `activating` are referred to as _activating_ annotations, and are used to link a resource that triggers an action with the resource(s) to change, enable or disable. The `target` of the activating annotation could be a commenting annotation, for which a user might click a corresponding UI element. In other scenarios the `target` could be the painting annotation of a 3D model, or an annotation that targets part of a model, or a region of a Canvas, or a point or segment of a Timeline, or any other annotation that a user could interact with (in whatever manner) to trigger an event. Even a volume of space in a Scene or an extent of time in a Container with `duration` could be the `target`, so that when the user "enters" that region or extent, something happens. +Annotations with the motivation `activating` are referred to as _activating_ annotations, and are used to link a resource that triggers an action with the resource(s) to change, enable or disable. The `target` of the activating annotation could be a commenting annotation, for which a user might click a corresponding UI element. In other scenarios the `target` could be the painting annotation of a 3D model, or an annotation that targets part of a model, or a region of a Canvas, or a point or segment of a Timeline, or any other annotation that a user could interact with (in whatever manner) to trigger an event. Even a volume of space in a Scene or an extent of time in a Container with `duration` could be the `target`. When the user triggers that volume or time extent - which might be the user entering that volume or the playhead reaching the extent - something happens. + +> TODO - `target` is a cuboid volume (not a painted model) - one client might expect you to click on it (and show its edges somehow for affordance), another client might expect you to walk into it, and doesn't render the shape at all, it is just an invisible volume. Do we need an extension to distinguish the two interaction behaviors? The `body` of the annotation is then activated. This has different processing requirements depending on what the body is: @@ -2041,12 +2043,12 @@ Activating annotations are provided in a Container's `annotations` property. The An activating annotation has two additional optional properties: -* `enables`: For each Annotation or AnnotationPage in the value, remove the 'hidden' behavior if it has it. +* `enables`: For each Annotation or AnnotationPage in the value, remove the 'hidden' behavior if it has it. * `disables`: For each Annotation or AnnotationPage in the value, add the 'hidden' behavior if it does not have it. If the values are the `id` properties of painting annotations that paint models, `enables` makes them visible and `disables` hides them. If they paint Lights, `enables` turns them on and `disables` turns them off. -Referencing a Painting Annotation as the `body` of an activating annotation implicitly enables it, as if it had been listed in `enables`. The inverse is not always true - for example, referencing a Camera in `enables` removes the "hidden" `behavior` and therefore allows it to be included in the client's evaluation of what the default camera is, but does not perform the additional action of changing the viewport to that Camera. For Lights and Models in a Scene, the two are equivalent because no _additional_ processing behavior is provided by this specification. +For Lights and Models in a Scene, `enables` is equivalent to the Light or Model being activated (i.e., being the `body` of the activating annotation), because no _additional_ processing behavior is provided by this specification. For Cameras, `enables` removes the "hidden" `behavior` (allows the Camera to be used), but does not by itself make the Camera the active viewport. For many use cases, the activating annotations don't need bodies. The following example demonstrates a light switch that can be toggled on and off: @@ -2238,10 +2240,11 @@ The format of the `value` string is implementation-specific, and will depend on "source": "https://example.org/iiif/3d/painting-anno-for-music-box", "selector": [ { - "type": "AnimationSelector", + "type": "ResourceStateSelector", "value": "open-the-lid" } - ] + ], + "apply": ["playing"] } ] } @@ -2254,6 +2257,8 @@ The format of the `value` string is implementation-specific, and will depend on ] } ``` + + ### 3D Comments with Cameras In many complex 3D Scenes, it may not be clear what or how to look at a particular point of interest even when the commenting annotation targets a particular point. The view may be occluded by parts of the model, or other models in the Scene. In the following example, the user can explore the Scene freely, but when they select a particular comment, a specific Camera that was previously hidden (unavailable to the user) is activated, moving the user (i.e., setting the viewport) to a chosen position suitable for looking at the point of interest: diff --git a/source/presentation/4.0/model.md b/source/presentation/4.0/model.md index 71572a609..3804eb800 100644 --- a/source/presentation/4.0/model.md +++ b/source/presentation/4.0/model.md @@ -1246,7 +1246,7 @@ Disjoint with `thumbnail-nav` and `no-nav`.| | `thumbnail-nav`{: style="white-space:nowrap;"} | Valid only on Ranges. Ranges that have this behavior _MAY_ be used by the client to present an alternative navigation or overview based on thumbnails, such as regular keyframes along a timeline for a video, or sections of a long scroll. Clients _SHOULD NOT_ use them to generate a conventional table of contents. Child Ranges of a Range with this behavior _MUST_ have a suitable `thumbnail` property. Disjoint with `sequence` and `no-nav`.| | `no-nav` | Valid only on Ranges. Ranges that have this behavior _MUST NOT_ be displayed to the user in a navigation hierarchy. This allows for Ranges to be present that capture unnamed regions with no interesting content, such as the set of blank pages at the beginning of a book, or dead air between parts of a performance, that are still part of the Manifest but do not need to be navigated to directly. Disjoint with `sequence` and `thumbnail-nav`.| | | **Miscellaneous Behaviors** | -| `hidden`{: #hidden-value} | Valid on Annotation Collections, Annotation Pages, Annotations, Specific Resources, Lights, Cameras and Choices. If this behavior is provided, then the client _SHOULD NOT_ render the resource by default, but allow the user to turn it on and off. This behavior does not inherit, as it is not valid on Collections, Manifests, Ranges or Canvases. | +| `hidden`{: #hidden-value} | Valid on Annotation Collections, Annotation Pages, Annotations, Specific Resources, Lights, Cameras and Choices. If this behavior is provided, then the client _SHOULD NOT_ render the resource by default, but allow the user to turn it on and off. This behavior does not inherit, as it is not valid on Collections, Manifests, Ranges or Canvases. TODO - this needs to talk about `hidden` on an activating annotation, which is not a visible (painted) resource. | {: .api-table #table-behavior} {% include api/code_header.html %} @@ -1342,6 +1342,11 @@ The value _MUST_ be a positive floating point number. TODO +does not reset the Container in any way, independent of where you are in a Container's `duration`, if you enable a video that is painted from t1-t2 then it will be playing if not paused and playhead is t1 < p < t2 +For non-duration containers it just appears and starts playing. +Activating a video again does nothing if already activated/enabled + + ### exclude {: #exclude} @@ -1793,20 +1798,9 @@ Additional motivations may be added to the Annotation to further clarify the int | ----- | ----------- | | `painting` | Resources associated with a Container by an Annotation that has the `motivation` value `painting` _MUST_ be presented to the user as the representation of the Container. The content can be thought of as being _of_ the Container. The use of this motivation with target resources other than Containers is undefined. For example, an Annotation that has the `motivation` value `painting`, a body of an Image and the target of a Canvas is an instruction to present that Image as (part of) the visual representation of the Canvas. Similarly, a textual body is to be presented as (part of) the visual representation of the Container and not positioned in some other part of the user interface.| | `supplementing` | Resources associated with a Container by an Annotation that has the `motivation` value `supplementing` _MAY_ be presented to the user as part of the representation of the Container, or _MAY_ be presented in a different part of the user interface. The content can be thought of as being _from_ the Container. The use of this motivation with target resources other than Containers is undefined. For example, an Annotation that has the `motivation` value `supplementing`, a body of an Image and the target of part of a Canvas is an instruction to present that Image to the user either in the Canvas's rendering area or somewhere associated with it, and could be used to present an easier to read representation of a diagram. Similarly, a textual body is to be presented either in the targeted region of the Container or otherwise associated with it, and might be OCR, a manual transcription or a translation of handwritten text, or captions for what is being said in a Timeline with audio content. | -| `activating` | Annotations with the motivation `activating` are referred to as _activating_ annotations, and are used to link a resource that triggers an action with the resource(s) to change, enable or disable. An annotation with the motivation `activating` has any valid IIIF Resource, or list of IIIF resources, or references to IIIF resources as its `target` property, and any potentially interactive resource as its `body`. It indicates that a user interaction will trigger a change in a Container, or play a named animation in a Model. If the `body` of the Annotation is of type `TextualBody` and the `target` is of type `SpecificResource` with a `selector` property of type `AnimationSelector`, then the client offers a UI such that when the user selects an interactive element labelled by the TextualBody, the named animation in the model painted by the `source` is played. | +| `activating` | Annotations with the motivation `activating` are referred to as _activating_ annotations, and are used to link a resource that triggers an action with the resource(s) to change, enable or disable. See [Dynamic Content]() for processing. | {: .api-table #table-motivations} -#### Activating Annotations - -The client must render the target resource as an interactive element in the user interface, which the user can trigger (e.g., clicking, selecting, entering). - -The `body` of the annotation is then activated. This specification defines the following client behaviors; others may me found in the [IIIF Cookbook][ref]. - -* If the body is a reference to a Painting Annotation: - * if the annotation has the `behavior` "hidden", then remove "hidden" from the `behavior`. - * if the annotation paints a Camera, make that Camera the active Camera (i.e., make this the viewport) (see [ref]). -* If the body is a SpecificResource with a `selector` property with the type "AnimationSelector", play the animation named by the `value` property of the Selector. (see [ref]). - ### navDate {: #navDate} @@ -2958,6 +2952,61 @@ A number (floating point) giving the z coordinate of the point, relative to the { "z": 100 } ``` +## Dynamic Content +{: #dynamic-content} + +An annotation with the motivation `activating` has any valid IIIF Resource, or list of IIIF resources, or references to IIIF resources as its `body` property, and any potentially interactable resource as its `target`. The `body` is activated by the `target`. + +There are two categories of activating annotation targets (interactable things): + +* Annotations - content of the container (a painting annotation rendered in the Scene) or content that targets the container (a commenting annotation, a map pin... which may have off-Container representations in a UI eg comments in a side panel). +* Extents of Containers - a volume of a Scene, region of a Canvas, or interval of time in any Container with a `duration` (the client may or may not render these "hit boxes") + +How the client makes these interactable is client-dependent. + +Need to cover: + +Clients supporting dynamic content need to support + + - non-painting annotations e.g., commenting annos (and other annos that usually have textual bodies that could be made clickable by a client, or map pin markers, etc) + - painted resources such as models + - volumes + - time extents + - other activating annos + +This specification defines the following client behaviors; others may be found in the [IIIF Cookbook][ref]. + +### Showing and hiding content + +If the body is a reference to a Painting Annotation or a non-painting , the client must render the `target` resource as an interactive element in the user interface, which the user (or _Container time_) can trigger (e.g., clicking, selecting, entering). The `body` of the annotation is then activated by this interaction. + + * if the annotation has the `behavior` "hidden", then remove "hidden" from the `behavior`. + +(example: click the comment, object appears, light goes on) + +enables and disables + +### Activating Lights + +You can just use enables and disables + +### Activating Cameras + + * if the annotation paints a Camera, make that Camera the active Camera (i.e., make this the viewport) (see [ref]). + +### Playing animations + +If the `body` is of type `SpecificResource` with a `selector` property of type `AnimationSelector`, the named animation in the model painted by the `source` is played when the `target` is activated. +* If the body is a SpecificResource with a `selector` property with the type "AnimationSelector", play the animation named by the `value` property of the Selector. (see [ref]). + + + +scope + + + + + ## JSON-LD and Extensions {: #json-ld-and-extensions} diff --git a/source/presentation/4.0/scratch.md b/source/presentation/4.0/scratch.md index 5ff5ad0a3..0caea0c29 100644 --- a/source/presentation/4.0/scratch.md +++ b/source/presentation/4.0/scratch.md @@ -229,3 +229,30 @@ What to do about activating annos in the introduced content? } ``` +Activating by entering (or interacting with) a volume: + +```json +{ + "id": "https://example.org/iiif/3d/anno9", + "type": "Annotation", + "motivation": ["activating"], + "target": [ + { + "id": "https://example.org/iiif/3d/anno9", + "type": "SpecificResource", + "selector": [ + { + "type": "WktSelector", + "wktLiteral": "POLYHEDRALSURFACE Z (blah blah)" + } + ], + "source": "https://example.org/iiif/3d/Scene1" + } + ], + "body": [ + { + "type": "JSONPatch that turns on the light" + } + ] +} +``` \ No newline at end of file From dcc18a97c74b88344fbea152c31ff18feebc9c68 Mon Sep 17 00:00:00 2001 From: tomcrane Date: Wed, 10 Dec 2025 13:02:59 -0500 Subject: [PATCH 2/6] model text for activating --- source/presentation/4.0/model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/presentation/4.0/model.md b/source/presentation/4.0/model.md index 3804eb800..be5593979 100644 --- a/source/presentation/4.0/model.md +++ b/source/presentation/4.0/model.md @@ -2999,7 +2999,7 @@ You can just use enables and disables If the `body` is of type `SpecificResource` with a `selector` property of type `AnimationSelector`, the named animation in the model painted by the `source` is played when the `target` is activated. * If the body is a SpecificResource with a `selector` property with the type "AnimationSelector", play the animation named by the `value` property of the Selector. (see [ref]). - +How do you stop the animation? And if you can stop it, what happens - reset? If you activate it again, does it resume or restart? scope From 9102c220bb5429501dee63505d537f8473cf17c4 Mon Sep 17 00:00:00 2001 From: tomcrane Date: Wed, 10 Dec 2025 16:04:08 -0500 Subject: [PATCH 3/6] before rewriting enables-disables --- source/presentation/4.0/index.md | 34 +++++-- source/presentation/4.0/model.md | 36 ++++++++ source/presentation/4.0/scratch.md | 141 ++++++++++++++++++++++++++++- 3 files changed, 204 insertions(+), 7 deletions(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index 39523d7d9..618bda1a4 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -2010,7 +2010,19 @@ Annotations with the motivation `activating` are referred to as _activating_ ann The `body` of the annotation is then activated. This has different processing requirements depending on what the body is: -* If the body is a reference to a Painting Annotation: +TODO introduce `action` here + +The body of the activating annotation is always a SpecificResource with an `action` property + +Perform the action(s) in sequence - see action ^^ + +``` +for specficResource in body + for action in specficResource.action + do the thing +``` +TODO delete this bit +* If the `source` is a reference to a Painting Annotation: * if the annotation has the `behavior` "hidden", then remove "hidden" from the `behavior`. * if the annotation paints a Camera, make that Camera the active Camera (i.e., make this the viewport) (see [ref]). * If the body is a SpecificResource with a `selector` property with the type "AnimationSelector", play the animation named by the `value` property of the Selector. (see [ref]). @@ -2031,8 +2043,9 @@ Activating annotations are provided in a Container's `annotations` property. The ], "body": [ { - "id": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", - "type": "Annotation" + "type": "SpecificResource", + "source": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", + "action": ["show", "enable", "select"] } ] } @@ -2240,11 +2253,11 @@ The format of the `value` string is implementation-specific, and will depend on "source": "https://example.org/iiif/3d/painting-anno-for-music-box", "selector": [ { - "type": "ResourceStateSelector", + "type": "AnimationSelector", "value": "open-the-lid" } ], - "apply": ["playing"] + "action": ["stop", "reset"] } ] } @@ -2263,6 +2276,9 @@ The format of the `value` string is implementation-specific, and will depend on In many complex 3D Scenes, it may not be clear what or how to look at a particular point of interest even when the commenting annotation targets a particular point. The view may be occluded by parts of the model, or other models in the Scene. In the following example, the user can explore the Scene freely, but when they select a particular comment, a specific Camera that was previously hidden (unavailable to the user) is activated, moving the user (i.e., setting the viewport) to a chosen position suitable for looking at the point of interest: +TODO discuss the verbose action form for comparison with above examples +THEN show the `scope` property as a short form of the same + ```jsonc { "id": "https://example.org/iiif/3d/whale_comment_scope_content_state.json", @@ -2351,6 +2367,12 @@ In many complex 3D Scenes, it may not be clear what or how to look at a particul "type": "Annotation", "motivation": ["commenting"], "bodyValue": "Mandibular tooth", + "scope": [ // equivalent to below + { // + "id": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", // + "type": "Annotation" // + } // + ], // "target": { // SpecificResource with PointSelector } @@ -2364,7 +2386,7 @@ In many complex 3D Scenes, it may not be clear what or how to look at a particul // SpecificResource with PointSelector } }, - { + { // see scope above "id": "https://example.org/iiif/3d/anno9", "type": "Annotation", "motivation": ["activating"], diff --git a/source/presentation/4.0/model.md b/source/presentation/4.0/model.md index be5593979..b86aa4081 100644 --- a/source/presentation/4.0/model.md +++ b/source/presentation/4.0/model.md @@ -1129,6 +1129,33 @@ The value of `accompanyingContainer` _MUST_ be a JSON object with the `id` and ` } ``` +### action +{: #action} + +Only valid inside activating annotations + +body of the activating anno is an ordered list of SpecificResource + +...which may have selectors eg for AnimationSelector +...but may just have action which are processed in order; client performs the action on the source. + +values are: + + * enable + * disable + * show + * hide + * reset + * stop + * start + + +* A Specific Resource _MAY_ have the `action` property.
+ Clients _SHOULD_ process the `action` property on Specific Resources. +* Other types of resource _MUST NOT_ have the `action` property.
+ Clients _SHOULD_ ignore `action` on other types of resource. + + ### angle {: #angle} @@ -1569,6 +1596,8 @@ The value of the `quantityValue` property of the Quantity _MUST_ be between 0.0 ### interactionMode {: #interactionMode} +TODO clarify how `interactionMode` corresponds to `action` + A set of features that guide or limit user interaction with content within a Container that the publisher of the content would prefer the client to use when presenting the resource. This specification defines values in the table below that guide interactions with Cameras within a Scene. Other values for other Container types or specifying other interaction modes for 3D content may be defined externally as an [extension][prezi30-ldce]. For interaction modes pertaining to Cameras within a Scene, the client _SHOULD_ use `interactionMode` to determine the user experience features and approaches whereby users are permitted to change or adjust Cameras when viewing content within a Scene (e.g., orbiting around the scene or locking the user to a first-person perspective). When more than one interaction mode is present, the client _SHOULD_ pick the first interaction mode that the client is capable of supporting. @@ -2976,6 +3005,13 @@ Clients supporting dynamic content need to support This specification defines the following client behaviors; others may be found in the [IIIF Cookbook][ref]. +> enables first then disables (?) + +breaking change +anno `body` => Ordered List, redefine in context +will break any `"body": {}` annos + + ### Showing and hiding content If the body is a reference to a Painting Annotation or a non-painting , the client must render the `target` resource as an interactive element in the user interface, which the user (or _Container time_) can trigger (e.g., clicking, selecting, entering). The `body` of the annotation is then activated by this interaction. diff --git a/source/presentation/4.0/scratch.md b/source/presentation/4.0/scratch.md index 0caea0c29..cbb79d763 100644 --- a/source/presentation/4.0/scratch.md +++ b/source/presentation/4.0/scratch.md @@ -229,7 +229,7 @@ What to do about activating annos in the introduced content? } ``` -Activating by entering (or interacting with) a volume: +# Activating by entering (or interacting with) a volume: ```json { @@ -255,4 +255,143 @@ Activating by entering (or interacting with) a volume: } ] } +``` + + +# Controlling a video that is not painted into duration (or not even painted at all) + +```json +{ + "id": "https://example.org/iiif/3d/box-opening-activating-anno", + "type": "Annotation", + "motivation": ["activating"], + "disables": ["https://example.org/iiif/3d/commenting-anno-for-video"], + "target": [ + { + "id": "https://example.org/iiif/3d/third-man-from-left", + "type": "Annotation" + } + ], + "body": [ + { + "type": "SpecificResource", + "source": "https://example.org/iiif/3d/commenting-anno-for-video", + "action": ["stop", "reset"] + } + ] +} +``` + +# actions for disable and enable (yes) + +```json +{ + "id": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2", + "type": "Annotation", + "motivation": [ + "activating" + ], + "target": "https://example.org/iiif/painting-annotation/lightswitch-1", + "body": [ + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2", + "action": ["disable"] + }, + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", + "action": ["enable"] + }, + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4", + "action": ["show"] + } + ] +}, +``` + +# Lightswitch enables and disables as props (no) + +```json +{ + "id": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2", + "type": "Annotation", + "motivation": [ + "activating" + ], + "target": "https://example.org/iiif/painting-annotation/lightswitch-1", + "disables": [ + "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2" + ], + "enables": [ + "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", + "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4" + ] +}, +``` + +# Animation with action + +```json +{ + "id": "https://example.org/iiif/3d/box-opening-activating-anno", + "type": "Annotation", + "motivation": ["activating"], + "target": [ + { + "id": "https://example.org/iiif/3d/box-opening-commenting-anno", + "type": "Annotation" + } + ], + "body": [ + { + "type": "SpecificResource", + "source": "https://example.org/iiif/3d/painting-anno-for-music-box", + "selector": [ + { + "type": "AnimationSelector", + "value": "open-the-lid" + } + ], + "action": ["start"] + } + ] +} +``` + +# Verbose form of scope + +```json +{ + "id": "https://example.org/iiif/3d/anno9", + "type": "Annotation", + "motivation": ["activating"], + "target": [ + { + "id": "https://example.org/iiif/3d/commenting-anno-for-mandibular-tooth", + "type": "Annotation" + } + ], + "body": [ + { + "type": "SpecificResource", + "source": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", + "action": ["show", "enable", "select"] + } + ] +} + +``` + +...is equivalent to this on the commenting anno: + +```jsonc + "scope": [ // <= ["show", "enable", "select"] + { + "id": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", + "type": "Annotation" + } + ], ``` \ No newline at end of file From b254ec355e737a875542b66658e0ed3210b1fb0d Mon Sep 17 00:00:00 2001 From: tomcrane Date: Wed, 10 Dec 2025 17:11:25 -0500 Subject: [PATCH 4/6] action everywhere --- source/presentation/4.0/index.md | 353 ++++++++++++++++++------------- source/presentation/4.0/model.md | 37 +--- 2 files changed, 218 insertions(+), 172 deletions(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index 618bda1a4..66b1ddf51 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -1270,12 +1270,11 @@ The Light is green and has a position, but has its default orientation of lookin }, "target": { "type": "SpecificResource", - "source": [ - { - "id": "https://example.org/iiif/scene1/page/p1/1", - "type": "Scene" - } - ], + "source": + { + "id": "https://example.org/iiif/scene1/page/p1/1", + "type": "Scene" + }, "selector": [ { "type": "PointSelector", @@ -1290,48 +1289,51 @@ The Light is green and has a position, but has its default orientation of lookin "id": "https://example.org/iiif/3d/anno2", "type": "Annotation", "motivation": ["painting"], - "body": { - "id": "https://example.org/iiif/3d/cameras/1", - "type": "PerspectiveCamera", - "label": {"en": ["Perspective Camera 1"]}, - "lookAt": { - "id": "https://example.org/iiif/3d/anno1", - "type": "Annotation" - }, - "near": 1, - "far": 100, - "fieldOfView": 50 - }, - "target": { - "type": "SpecificResource", - "source": [ + "body": [ + { + "id": "https://example.org/iiif/3d/cameras/1", + "type": "PerspectiveCamera", + "label": {"en": ["Perspective Camera 1"]}, + "lookAt": { + "id": "https://example.org/iiif/3d/anno1", + "type": "Annotation" + }, + "near": 1, + "far": 100, + "fieldOfView": 50 + } + ], + "target": [ + { + "type": "SpecificResource", + "source": { "id": "https://example.org/iiif/scene1/page/p1/1", "type": "Scene" - } - ], - "selector": [ - { - "type": "PointSelector", - "x": 0.0, - "y": 6.0, - "z": 10.0 - } - ] - } + }, + "selector": [ + { + "type": "PointSelector", + "x": 0.0, + "y": 6.0, + "z": 10.0 + } + ] + } + ] }, { "id": "https://example.org/iiif/3d/anno2", "type": "Annotation", "motivation": ["painting"], - "body": { + "body": [{ "id": "https://example.org/iiif/3d/lights/1", "type": "SpotLight", "label": {"en": ["Spot Light 1"]}, "angle": 90.0, "color": "#A0FFA0" - }, - "target": { + }], + "target": [{ "type": "SpecificResource", "source": { "id": "https://example.org/iiif/scene1/page/p1/1", @@ -1345,7 +1347,7 @@ The Light is green and has a position, but has its default orientation of lookin "z": 1.0 } ] - } + }] } ] } @@ -1392,13 +1394,13 @@ This example is a Manifest with a single Scene with multiple models painted into "id": "https://example.org/iiif/3d/anno1", "type": "Annotation", "motivation": ["painting"], - "body": { + "body": [{ "id": "https://raw.githubusercontent.com/IIIF/3d/main/assets/chess/pawn.glb", "label": {"en": ["Pawn 1"]}, "type": "Model", "format": "model/gltf-binary" - }, - "target": { + }], + "target": [{ "type": "SpecificResource", "source": { "id": "https://example.org/iiif/scene1/page/p1/1", @@ -1412,22 +1414,21 @@ This example is a Manifest with a single Scene with multiple models painted into "z": 0.0 } ] - } + }] }, { "id": "https://example.org/iiif/3d/anno1", "type": "Annotation", "motivation": ["painting"], - "body": { + "body": [{ "type": "SpecificResource", - "source": [ + "source": { "id": "https://raw.githubusercontent.com/IIIF/3d/main/assets/chess/pawn.glb", "label": {"en": ["Pawn 2 tipped over"]}, "type": "Model", "format": "model/gltf-binary" - } - ], + }, "transform": [ { "type": "RotateTransform", @@ -1442,8 +1443,8 @@ This example is a Manifest with a single Scene with multiple models painted into "z": 0.0 } ] - }, - "target": { + }], + "target": [{ "type": "SpecificResource", "source": { "id": "https://example.org/iiif/scene1/page/p1/1", @@ -1457,23 +1458,22 @@ This example is a Manifest with a single Scene with multiple models painted into "z": 3.0 } ] - } + }] }, { "id": "https://example.org/iiif/3d/anno1", "type": "Annotation", "motivation": ["painting"], "exclude": ["Audio", "Lights"], - "body": { + "body": [{ "type": "SpecificResource", - "source": [ + "source": { "id": "https://raw.githubusercontent.com/IIIF/3d/main/assets/chess/queen.glb", "label": {"en": ["Queen"]}, "type": "Model", "format": "model/gltf-binary" - } - ], + }, "transform": [ { "type": "ScaleTransform", @@ -1482,8 +1482,8 @@ This example is a Manifest with a single Scene with multiple models painted into "z": 1.5 }, ] - }, - "target": { + }], + "target": [{ "type": "SpecificResource", "source": { "id": "https://example.org/iiif/scene1/page/p1/1", @@ -1498,6 +1498,7 @@ This example is a Manifest with a single Scene with multiple models painted into } ] } + ] } ] } @@ -1558,7 +1559,7 @@ In this example, the audio content resources have durations that do not match th "id": "https://example.org/iiif/3d/anno1", "type": "Annotation", "motivation": ["painting"], - "body": { + "body": [{ "id": "https://example.org/iiif/audio/1", "type": "AmbientAudio", "source": { @@ -1572,15 +1573,20 @@ In this example, the audio content resources have durations that do not match th "unit": "relative", "quantityValue": 0.1 } - }, - "target": "https://example.org/iiif/scene1" + }], + "target": [ + { + "id": "https://example.org/iiif/scene1", + "type": "Scene" + } + ] }, { "id": "https://example.org/iiif/3d/anno2", "type": "Annotation", "motivation": ["painting"], "timeMode": "trim", - "body": { + "body": [{ "id": "https://example.org/iiif/audio/2", "type": "PointAudio", "source": { @@ -1594,16 +1600,15 @@ In this example, the audio content resources have durations that do not match th "unit": "relative", "quantityValue": 0.2 } - }, - "target": { + }], + "target": [{ "id": "https://example.org/iiif/selectors/anno2", "type": "SpecificResource", - "source": [ + "source": { "id": "https://example.org/iiif/scene1", "type": "Scene" - } - ], + }, "selector": [ { "id": "https://example.org/uuid/9fbd580b-895b-41b9-974a-1553329037f2", @@ -1618,14 +1623,14 @@ In this example, the audio content resources have durations that do not match th } } ] - } + }] }, { "id": "https://example.org/iiif/3d/anno3", "type": "Annotation", "motivation": ["painting"], "timeMode": "loop", - "body": { + "body": [{ "id": "https://example.org/iiif/audio/3", "type": "SpotAudio", "source": { @@ -1641,8 +1646,13 @@ In this example, the audio content resources have durations that do not match th "quantityValue": 0.3 }, "lookAt": "https://example.org/iiif/scene1" - }, - "target": "https://example.org/iiif/scene1#xyz=3,0,-2&t=30,60" + }], + "target": [ + { + "id": "https://example.org/iiif/scene1#xyz=3,0,-2&t=30,60", + "type": "Scene" + } + ] } ], "annotations": [ @@ -1650,24 +1660,29 @@ In this example, the audio content resources have durations that do not match th "id": "https://example.org/iiif/3d/commenting", "type": "Annotation", "motivation": ["commenting"], - "bodyValue": "This is the point when the percussion stops playing and the tuba begins playing.", - "target": { + "body": [ + { + "type": "TextualBody", + "value": "This is the point when the percussion stops playing and the tuba begins playing." + } + ], + "target": [{ "type": "SpecificResource", - "source": [ + "source": { "id": "https://example.org/iiif/scene1", "type": "Scene" } - ], + , "selector": [ { "type": "PointSelector", "instant": 30.0 } ] - } - }, - ], + }] + } + ] } ] } @@ -1865,14 +1880,14 @@ A comment on a Canvas can target a non-rectangular area. This example uses a `S "id": "https://example.org/iiif/presentation/examples/commenting/anno/2", "type": "Annotation", "motivation": [ "commenting" ], - "body": { + "body": [{ "id": "https://example.org/iiif/presentation/examples/commenting/anno/2/person2", "type": "TextualBody", "language": "en", "format": "text/plain", "value": "Note the expressive eyes of the subject of this painting." - }, - "target": { + }], + "target": [{ "type": "SpecificResource", "source": { "id": "https://example.org/iiif/presentation/examples/commenting/canvas/2", @@ -1885,7 +1900,7 @@ A comment on a Canvas can target a non-rectangular area. This example uses a `S "value": " ... " } ] - } + }] } ``` @@ -2012,7 +2027,7 @@ The `body` of the annotation is then activated. This has different processing re TODO introduce `action` here -The body of the activating annotation is always a SpecificResource with an `action` property +The body of the activating annotation is always a SpecificResource with an `action` property. The `source` is the resource to be activated or deactivated in some way, and the `action` ... e.g., show, hide, enable, disable, start, stop. Perform the action(s) in sequence - see action ^^ @@ -2054,16 +2069,7 @@ Activating annotations are provided in a Container's `annotations` property. The ### Showing and hiding resources -An activating annotation has two additional optional properties: - -* `enables`: For each Annotation or AnnotationPage in the value, remove the 'hidden' behavior if it has it. -* `disables`: For each Annotation or AnnotationPage in the value, add the 'hidden' behavior if it does not have it. - -If the values are the `id` properties of painting annotations that paint models, `enables` makes them visible and `disables` hides them. If they paint Lights, `enables` turns them on and `disables` turns them off. - -For Lights and Models in a Scene, `enables` is equivalent to the Light or Model being activated (i.e., being the `body` of the activating annotation), because no _additional_ processing behavior is provided by this specification. For Cameras, `enables` removes the "hidden" `behavior` (allows the Camera to be used), but does not by itself make the Camera the active viewport. - -For many use cases, the activating annotations don't need bodies. The following example demonstrates a light switch that can be toggled on and off: +The following example demonstrates a light switch that can be toggled on and off: ```jsonc { @@ -2140,12 +2146,22 @@ For many use cases, the activating annotations don't need bodies. The following "activating" ], "target": "https://example.org/iiif/painting-annotation/lightswitch-1", - "disables": [ - "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2" - ], - "enables": [ - "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", - "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4" + "body": [ + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", + "action": ["enable"] + }, + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4", + "action": ["show"] + }, + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2", + "action": ["disable"] + } ] }, { @@ -2155,14 +2171,24 @@ For many use cases, the activating annotations don't need bodies. The following "activating" ], "target": "https://example.org/iiif/painting-annotation/lightswitch-1", - "disables": [ - "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", - "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4" - ], - "enables": [ - "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2" + "body": [ + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", + "action": ["disable"] + }, + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4", + "action": ["hide"] + }, + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2", + "action": ["enable"] + } ], - "behavior": ["hidden"] + "behavior": ["disabled"] } ] } @@ -2172,9 +2198,10 @@ For many use cases, the activating annotations don't need bodies. The following } ``` -* Initially, a model of a light switch is painted into the Scene. A PointLight is also painted, but with the `behavior` "hidden", which means it is inactive (i.e., off). A commenting annotation with the text "Click the switch to turn the light on or off" targets the light switch. An activating annotation targets the commenting annotation, so that user interaction with the commenting annotation will trigger the activating annotation. This activating annotation has no `body`, but it does have `enables` with values that are the `id` properties of the painting annotation for the light switch model, and the activating annotation that turns the light off. It also has a `disables` with the value of its own `id` - i.e., it disables _itself_. A further activating annotation has the opposite effect. Initially this has the `behavior` "hidden" - which means it is inactive. It also targets the commenting annotation, but has no effect while hidden. -* When the user interacts with the light switch model, the client processes any activating annotations that target it and are not hidden. In this case, the first activating annotation is triggered because while both target the switch, only the first is not hidden. This activation `enables` the light (i.e., removing its "hidden" `behavior` and therefore turning it on) and the other activating annotation, and `disables` itself. -* If the user clicks the light again, the client again processes any activating annotations that target it and are not hidden. This time the second annotation is the active one - and it `disables` the light (turning it off) and itself, and enables the first activating annotation again. +* Initially, a model of a light switch is painted into the Scene. A PointLight is also painted, but with the `behavior` "hidden", which means it is inactive (i.e., off). A commenting annotation with the text "Click the switch to turn the light on or off" targets the light switch. An activating annotation targets the painting annotation that paints the switch, so that user interaction with the light switch will trigger the activating annotation. This activating annotation has a `body` property with three Specific Resources. The first enables the "off" activating annotation, the second shows the PointLight, and the last disables the activating annotation _itself_ - this activating annotation can no longer be activated by a user interaction with the light switch model (its `target`). +* A further activating annotation has the opposite effect. Initially this has the `behavior` "disabled" - which means it is inactive. It also targets the painting annotation, but has no effect while disabled. +* When the user interacts with the light switch model, the client processes any activating annotations that target it and are enabled. In this case, the first activating annotation is triggered because while both target the switch, only the first is enabled. This activation shows the light (i.e., removes its "hidden" `behavior` and therefore turning it on) and enables the other activating annotation, and disables itself. +* If the user clicks the light again, the client again processes any activating annotations that target it and are not disabled. This time the second activating annotation is the enabled one - and it hides the light (turning it off) and disables itself, and enables the first activating annotation again. * Subsequent clicks simply alternate between these two states, indefinitely. @@ -2257,7 +2284,7 @@ The format of the `value` string is implementation-specific, and will depend on "value": "open-the-lid" } ], - "action": ["stop", "reset"] + "action": ["start"] } ] } @@ -2276,8 +2303,6 @@ The format of the `value` string is implementation-specific, and will depend on In many complex 3D Scenes, it may not be clear what or how to look at a particular point of interest even when the commenting annotation targets a particular point. The view may be occluded by parts of the model, or other models in the Scene. In the following example, the user can explore the Scene freely, but when they select a particular comment, a specific Camera that was previously hidden (unavailable to the user) is activated, moving the user (i.e., setting the viewport) to a chosen position suitable for looking at the point of interest: -TODO discuss the verbose action form for comparison with above examples -THEN show the `scope` property as a short form of the same ```jsonc { @@ -2311,34 +2336,33 @@ THEN show the `scope` property as a short form of the same "type": "Annotation", "motivation": ["painting"], "behavior": ["hidden"], - "body": { - "type": "SpecificResource", - "source": [ - { - "id": "https://example.org/iiif/3d/cameras/1", - "type": "PerspectiveCamera", - "label": {"en": ["Perspective Camera Pointed At Front of Cranium and Mandible"]}, - "fieldOfView": 50.0, - "near": 0.10, - "far": 2000.0 - } - ] - }, - "target": { - "type": "SpecificResource", - "source": [ - { - "id": "https://example.org/iiif/scene1", - "type": "Scene" - } - ], - "selector": [ - { - "type": "PointSelector", - "x": 0.0, "y": 0.15, "z": 0.75 - } - ] - } + "body": [ + { + "id": "https://example.org/iiif/3d/cameras/1", + "type": "PerspectiveCamera", + "label": {"en": ["Perspective Camera Pointed At Front of Cranium and Mandible"]}, + "fieldOfView": 50.0, + "near": 0.10, + "far": 2000.0 + } + ], + "target": [ + { + "type": "SpecificResource", + "source": [ + { + "id": "https://example.org/iiif/scene1", + "type": "Scene" + } + ], + "selector": [ + { + "type": "PointSelector", + "x": 0.0, "y": 0.15, "z": 0.75 + } + ] + } + ] }, { "id": "https://example.org/iiif/3d/anno2", @@ -2366,13 +2390,12 @@ THEN show the `scope` property as a short form of the same "id": "https://example.org/iiif/3d/commenting-anno-for-mandibular-tooth", "type": "Annotation", "motivation": ["commenting"], - "bodyValue": "Mandibular tooth", - "scope": [ // equivalent to below - { // - "id": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", // - "type": "Annotation" // - } // - ], // + "body": [ + { + "type": "TextualBody", + "value": "Mandibular tooth" + } + ], "target": { // SpecificResource with PointSelector } @@ -2381,12 +2404,17 @@ THEN show the `scope` property as a short form of the same "id": "https://example.org/iiif/3d/commenting-anno-for-right-pterygoid-hamulus", "type": "Annotation", "motivation": ["commenting"], - "bodyValue": "Right pterygoid hamulus", + "body": [ + { + "type": "TextualBody", + "value": "Right pterygoid hamulus" + } + ], "target": { // SpecificResource with PointSelector } }, - { // see scope above + { "id": "https://example.org/iiif/3d/anno9", "type": "Annotation", "motivation": ["activating"], @@ -2398,8 +2426,9 @@ THEN show the `scope` property as a short form of the same ], "body": [ { - "id": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", - "type": "Annotation" + "type": "SpecificResource", + "source": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", + "action": ["show", "enable", "select"] } ] } @@ -2420,6 +2449,38 @@ Camera when annotation selected: Camera for annotation +#### Using scope to select a Camera + +The previous example can also be expressed in a more concise form by providing a reference to the Camera in the `scope` property of the commenting annotation. + +The commenting annotation now looks like this: + +```json +{ + "id": "https://example.org/iiif/3d/commenting-anno-for-mandibular-tooth", + "type": "Annotation", + "motivation": ["commenting"], + "bodyValue": "Mandibular tooth", + "scope": [ + { + "id": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", + "type": "Annotation" + } + ], + "target": { + // SpecificResource with PointSelector + } +}, +``` + +... and the activating annotation is no longer required. + +This only works for cameras. + +Repeat full example? no, link to external. + + + # Integration seeAlso, service(s), extensions diff --git a/source/presentation/4.0/model.md b/source/presentation/4.0/model.md index b86aa4081..8dfcaccd1 100644 --- a/source/presentation/4.0/model.md +++ b/source/presentation/4.0/model.md @@ -1132,7 +1132,7 @@ The value of `accompanyingContainer` _MUST_ be a JSON object with the `id` and ` ### action {: #action} -Only valid inside activating annotations +Only valid on SpecificResource when bodies of activating annotations body of the activating anno is an ordered list of SpecificResource @@ -1141,14 +1141,14 @@ body of the activating anno is an ordered list of SpecificResource values are: - * enable - * disable - * show - * hide - * reset - * stop - * start - + * enable (make selectable, or makes an activating anno triggerable) + * disable (inverse) + * show (removes behavior:hidden) + * hide (applies behavior:hidden) + * reset (rewind AV to beginning) + * stop (animations in models; time-based content resources that are not painted into duration) + * start (ditto) + * select (rarely used because scope) * A Specific Resource _MAY_ have the `action` property.
Clients _SHOULD_ process the `action` property on Specific Resources. @@ -1336,12 +1336,6 @@ The value _MUST_ be string, which defines an RGB color. It SHOULD be a hex value ``` -### disables -{: #disables} - -todo - - ### duration {: #duration} @@ -1364,17 +1358,6 @@ The value _MUST_ be a positive floating point number. ``` -### enables -{: #enables} - -TODO - -does not reset the Container in any way, independent of where you are in a Container's `duration`, if you enable a video that is painted from t1-t2 then it will be playing if not paused and playhead is t1 < p < t2 -For non-duration containers it just appears and starts playing. -Activating a video again does nothing if already activated/enabled - - - ### exclude {: #exclude} @@ -2984,6 +2967,8 @@ A number (floating point) giving the z coordinate of the point, relative to the ## Dynamic Content {: #dynamic-content} +TODO note that SpecificResource body `source` must exist in manifest elsewhere, is only a reference. + An annotation with the motivation `activating` has any valid IIIF Resource, or list of IIIF resources, or references to IIIF resources as its `body` property, and any potentially interactable resource as its `target`. The `body` is activated by the `target`. There are two categories of activating annotation targets (interactable things): From 7d2d0dabe59da5d6f6cf10c307f16b4df68c7d5b Mon Sep 17 00:00:00 2001 From: tomcrane Date: Thu, 11 Dec 2025 11:05:06 -0500 Subject: [PATCH 5/6] actions and activating annos --- source/presentation/4.0/index.md | 65 +++++++------------------------- source/presentation/4.0/model.md | 5 +++ 2 files changed, 18 insertions(+), 52 deletions(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index 66b1ddf51..f47bae4b2 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -2017,59 +2017,20 @@ The resource the user should be taken to is the `body` of the annotation, and th ## Activating Annotations -Sometimes it is necessary to modify the contents of a Container in the contexts of different annotations on that Container. This technique allows IIIF to be used for exhibitions, storytelling (fwd ref) and other interactive applications beyond simply conveying a set of static resources in a Container. +Sometimes it is necessary to modify the state of resources. Annotations with the motivation `activating` are referred to as _activating_ annotations, and are used to change resources from their initial state defined in the Manifest or from their current state. They allow IIIF to be used for interactive exhibitions, storytelling, digital dioramas and other interactive applications beyond simply conveying a set of static resources in a Container. -Annotations with the motivation `activating` are referred to as _activating_ annotations, and are used to link a resource that triggers an action with the resource(s) to change, enable or disable. The `target` of the activating annotation could be a commenting annotation, for which a user might click a corresponding UI element. In other scenarios the `target` could be the painting annotation of a 3D model, or an annotation that targets part of a model, or a region of a Canvas, or a point or segment of a Timeline, or any other annotation that a user could interact with (in whatever manner) to trigger an event. Even a volume of space in a Scene or an extent of time in a Container with `duration` could be the `target`. When the user triggers that volume or time extent - which might be the user entering that volume or the playhead reaching the extent - something happens. +The `target` of the activating annotation is the resource that triggers an action. This could be a commenting annotation, for which a user might click a corresponding UI element. In other scenarios the `target` could be the painting annotation of a 3D model, or an annotation that targets part of a model, or a region of a Canvas, or a point or segment of a Timeline, or any other annotation that a user could interact with (in whatever manner) to trigger an event. Even a volume of space in a Scene or an extent of time in a Container with `duration` could be the `target`. When that volume or time extent is triggered - which might be the user entering that volume or the playhead reaching the extent independently of user interaction - something happens. -> TODO - `target` is a cuboid volume (not a painted model) - one client might expect you to click on it (and show its edges somehow for affordance), another client might expect you to walk into it, and doesn't render the shape at all, it is just an invisible volume. Do we need an extension to distinguish the two interaction behaviors? +This specification does not define how a client indicates to a user that a resource is able to be interacted with. -The `body` of the annotation is then activated. This has different processing requirements depending on what the body is: +The body of the activating annotation is always an ordered list of Specific Resources, each with `source` and `action` properties. The `source` is the resource to be acted upon in some way, and the `action` property is an ordered list of named actions to perform on that resource. Valid values include "show", "hide", "enable", "disable", "start", "stop", "reset" and "select". -TODO introduce `action` here +Activating annotations are provided in a Container's `annotations` property. They can be mixed in with the commenting (or other interactive annotations) they target, or they can be in a separate Annotation Page. The client should evaluate all of the enabled activating annotations it can find. -The body of the activating annotation is always a SpecificResource with an `action` property. The `source` is the resource to be activated or deactivated in some way, and the `action` ... e.g., show, hide, enable, disable, start, stop. -Perform the action(s) in sequence - see action ^^ +### Hiding and disabling resources -``` -for specficResource in body - for action in specficResource.action - do the thing -``` -TODO delete this bit -* If the `source` is a reference to a Painting Annotation: - * if the annotation has the `behavior` "hidden", then remove "hidden" from the `behavior`. - * if the annotation paints a Camera, make that Camera the active Camera (i.e., make this the viewport) (see [ref]). -* If the body is a SpecificResource with a `selector` property with the type "AnimationSelector", play the animation named by the `value` property of the Selector. (see [ref]). -* Processing for other body types can be found in the [IIIF Cookbook][ref] - -Activating annotations are provided in a Container's `annotations` property. They can be mixed in with the commenting (or other interactive annotations) they target, or they can be in a separate AnnotationPage. The client should evaluate all the activating annotations it can find. - -```jsonc -{ - "id": "https://example.org/iiif/3d/anno9", - "type": "Annotation", - "motivation": ["activating"], - "target": [ - { - "id": "https://example.org/iiif/3d/commenting-anno-for-mandibular-tooth", - "type": "Annotation" - } - ], - "body": [ - { - "type": "SpecificResource", - "source": "https://example.org/iiif/3d/anno-that-paints-desired-camera-to-view-tooth", - "action": ["show", "enable", "select"] - } - ] -} -``` - - -### Showing and hiding resources - -The following example demonstrates a light switch that can be toggled on and off: +A resource with the `behavior` value "hidden" is not rendered by the client. A resource with the `behavior` value "disabled" is not available for user interaction and does not trigger any actions. The following example is a light switch that can be toggled on and off using activating annotations that result in these behaviors being applied or removed. It demonstrates a painted resource - a light - being shown and hidden, and activating annotations being enabled and disabled. Both of these are done by the client processing the action properties of the activating annotation bodies: the actions "show" and "hide" remove or add the behavior value "hidden", and the actions "enable" and "disable" modify the behavior value "disabled". ```jsonc { @@ -2146,7 +2107,12 @@ The following example demonstrates a light switch that can be toggled on and off "activating" ], "target": "https://example.org/iiif/painting-annotation/lightswitch-1", - "body": [ + "body": [ + { + "type": "SpecificResource", + "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2", + "action": ["disable"] + }, { "type": "SpecificResource", "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-off-3", @@ -2156,11 +2122,6 @@ The following example demonstrates a light switch that can be toggled on and off "type": "SpecificResource", "source": "https://example.org/iiif/scene/switch/scene-1/lights/point-light-4", "action": ["show"] - }, - { - "type": "SpecificResource", - "source": "https://example.org/iiif/scene/switch/scene-1/annos/1/activating-on-2", - "action": ["disable"] } ] }, diff --git a/source/presentation/4.0/model.md b/source/presentation/4.0/model.md index 8dfcaccd1..70c8d35b9 100644 --- a/source/presentation/4.0/model.md +++ b/source/presentation/4.0/model.md @@ -1139,6 +1139,11 @@ body of the activating anno is an ordered list of SpecificResource ...which may have selectors eg for AnimationSelector ...but may just have action which are processed in order; client performs the action on the source. +The client must perform all the actions; if it can't perform all of them it must not perform any. +If the activating annotation that is currently being processed is disabled as part of that processing, don't stop processing the ordered list, keep going through to the end. + +Only process one set of activating anno bodies at a time. If a body causes another activating anno to be triggered, queue up that activating anno and don't tackle it until you've finished processing all the bodies of the current one. + values are: * enable (make selectable, or makes an activating anno triggerable) From 85f9a498302e6b5fb64e0d4384dafed3fb071024 Mon Sep 17 00:00:00 2001 From: tomcrane Date: Thu, 11 Dec 2025 11:54:03 -0500 Subject: [PATCH 6/6] ImageApiSelector --- source/presentation/4.0/index.md | 106 +++++++++++++++++------------ source/presentation/4.0/model.md | 1 + source/presentation/4.0/scratch.md | 20 ++++++ 3 files changed, 82 insertions(+), 45 deletions(-) diff --git a/source/presentation/4.0/index.md b/source/presentation/4.0/index.md index f47bae4b2..824934f97 100644 --- a/source/presentation/4.0/index.md +++ b/source/presentation/4.0/index.md @@ -2170,9 +2170,9 @@ A resource with the `behavior` value "hidden" is not rendered by the client. A r Sometimes a model file has inbuilt animations. While a description of these is outside the scope of IIIF, because it is 3D-implementation-specific, as long as there is a way to refer to a model's animation(s) by name, we can connect the animation to IIIF resources. -This pattern is also achieved with activating annotations, except that the body of the activating annotation references a _named animation_ in the model. The `body` MUST be a SpecificResource, where the `source` is the Painting Annotation that paints the model, and the `selector` is of type `AnimationSelector` with the `value` being a string that corresponds to the animation in the model. +This pattern is also achieved with activating annotations, except that the body of the activating annotation references a _named animation_ in the model. The `body` is a Specific Resource, where the `source` is the Painting Annotation that paints the model, and the `selector` is of type `AnimationSelector` with the `value` being a string that corresponds to the name of the animation in the model. -The format of the `value` string is implementation-specific, and will depend on how different 3D formats support addressing of animations within models. The same model can be painted multiple times into the scene, and you might want to activate only one model's animation, thus we need to refer to the annotation that paints the model, not the model directly. +The format of the `value` string is implementation-specific, and will depend on how different 3D formats support addressing of animations within models. The same model can be painted multiple times into the scene, and you might want to activate only one painted instance of the model's animation, thus we need to refer to the annotation that paints the model, not the model directly. ```jsonc @@ -2215,7 +2215,7 @@ The format of the `value` string is implementation-specific, and will depend on "body": [ { "type": "TextualBody", - "value": "Click the box to open the lid" + "value": "Click me to open the lid" } ], "target": [ @@ -2262,7 +2262,7 @@ The format of the `value` string is implementation-specific, and will depend on ### 3D Comments with Cameras -In many complex 3D Scenes, it may not be clear what or how to look at a particular point of interest even when the commenting annotation targets a particular point. The view may be occluded by parts of the model, or other models in the Scene. In the following example, the user can explore the Scene freely, but when they select a particular comment, a specific Camera that was previously hidden (unavailable to the user) is activated, moving the user (i.e., setting the viewport) to a chosen position suitable for looking at the point of interest: +It is possible to associate a particular camera with a particular commenting annotation. In many complex 3D Scenes, it may not be clear from where to look at a particular point of interest. The view may be occluded by parts of the model, or other models in the Scene. In the following example, the user can explore the Scene freely, but when they select a particular comment, a specific Camera that was previously hidden (unavailable to the user) is activated, moving the user (i.e., setting the viewport) to a chosen position suitable for looking at the point of interest: ```jsonc @@ -2441,27 +2441,11 @@ This only works for cameras. Repeat full example? no, link to external. +### Interactivity, Guided Viewing and Storytelling -# Integration - -seeAlso, service(s), extensions -mention search, image api, auth - -profile for seeAlso - -partOf - +Activating annotations add explicit mechanisms for interactive user experiences such as guided viewing and storytelling. A narrative might comprise an Annotation Page of `commenting` annotations that target different parts of the Container, for example a guided tour of a painting or a map. For a Canvas or Timeline it is usually sufficient to leave the interactivity to the client; the fact that comments target different extents implies the client must offer some affordance for those comments (typically the user can click each one), and in response the client will move the current play point of the Timeline to the commenting annotation target, or pan and zoom the viewport to show the relevant part of an image. For 3D this may not be enough; a particular comment may only make sense from a certain viewpoint (i.e., Camera), or different steps of the story require different Lights to be active. -Talk about Content State and use the phrase "Content State Annotations" -(we think we now _don't_ need to write a content state 2 spec ???) - - - -# Interactivity, Guided Viewing and Storytelling - - -A narrative might comprise an AnnotationPage of `commenting` annotations that target different parts of the Container, for example a guided tour of a painting or a map. For a Canvas or Timeline it is usually sufficient to leave the interactivity to the client; the fact that comments target different extents implies the client must offer some affordance for those comments (typically the user can click each one), and in response the client will move the current play point of the Timeline to the commenting annotation target, or pan and zoom the viewport to show the relevant part of an image. For 3D this may not be enough; a particular comment may only make sense from a certain viewpoint (i.e., Camera), or different steps of the story require different Lights to be active. - -In a storytelling or exhibition scenario, the non-painting `annotations` might be carrying informative text, or even rich HTML bodies. They can be considered to be _steps_ in the story. The use of activating annotations (back ref) allows a precise storytelling experience to be specified, including: +In a storytelling or exhibition scenario, the non-painting `annotations` might be carrying informative text, or even rich HTML bodies. They can be considered to be _steps_ in the story. The use of activating annotations allows a precise storytelling experience to be specified, including: - providing a specific viewpoint for each step of the narrative (or even a choice of viewpoints) - modifying the lighting of the Scene for each step, for example shining a spotlight on a point of interest @@ -2470,25 +2454,13 @@ In a storytelling or exhibition scenario, the non-painting `annotations` might b All the annotations referred to by the activating annotations' `target` and `body` properties are already present in the Scene from the beginning. Initially, many of them may have the behavior `hidden`, invisible until activated. +Interactive examples are provided as recipes in the [IIIF Cookbook](link). -## The `sequence` behavior - -While all AnnotationPage `items` are inherently ordered, an Annotation Page with the `behavior` "sequence" is explicitly a narrative, and clients should prevent (dissuade) users from jumping about - the annotations, and the effects of them _activating_ other contents of the Container, are intended to be experienced in order and individually. Normally, a client might display all the comments in an AnnotationPage in a sidebar so they are all visible in the UI, but for an AnnotationPage with `behavior` "sequence" only show the currently active annotation text, and next and previous UI. +#### The `sequence` behavior -## Chains of activation +While all Annotation Page `items` are inherently ordered, an Annotation Page with the `behavior` "sequence" is explicitly a narrative, and clients should prevent (dissuade) users from jumping about - the annotations, and the effects of them _activating_ other contents of the Container, are intended to be experienced in order and individually. Normally, a client might display all the comments in an Annotation Page in a sidebar so they are all visible in the UI, but for an Annotation Page with `behavior` "sequence" only show the currently active annotation text, and next and previous UI. -Chaining together activating annotations can then allow the implementation of, at least: - -* Specific camera position to look at an Annotation -* Multi-step linear stories -* Animations, including as part of stories without disrupting the flow, and looping animations (they activate themselves) -* Interactive components such as light switches (enable/disable a light), jukeboxes (enable/disable Audio Emitter) - - -## Storytelling example - -* Something really cool that brings a lot of things together! # Conveying Physical Dimensions @@ -2505,17 +2477,65 @@ An extreme example of both physical dimension properties together is a Canvas sh -# Other stuff +# Integration + +seeAlso, service(s), extensions +mention search, image api, auth -## Embedded Content +profile for seeAlso -e.g., painting TextualBody on Canvas +partOf - +Talk about Content State and use the phrase "Content State Annotations" - how you transmit IIIF from software to software. + +## Authentication + +It is possible to include Image API service descriptions within the Manifest, and within those it is also possible to include links to the Authentication API's services that are needed to interact with the image content. The first time an Authentication API service is included within a Manifest, it _MUST_ be the complete description. Subsequent references _SHOULD_ be just the URI of the service, and clients are expected to look up the details from the full description by matching the URI. Clients _MUST_ anticipate situations where the Authentication service description in the Manifest is out of date: the source of truth is the Image Information document, or other system that references the Authentication API services. + + + + +# Other stuff ## Style ### Rotation +An image might not be correctly aligned with the Canvas, and require rotation as it is painted. In the following example, the image is painted with a 90-degree rotation. This example uses the ImageApiSelector to convey the number of degrees of the rotation. As this particular image has an image service, the client can use the Image API to request an image that has already been rotated on the server, or it can use the information in the ImageApiSelector to rotate the image itself. + +```json +{ + "id": "http://example.org/iiif/book1/annotation/anno1", + "type": "Annotation", + "motivation": ["painting"], + "body": [ + { + "type": "SpecificResource", + "source": { + "id": "http://example.org/iiif/book1-page1/my-image.jpg", + "type": "Image", + "service": { + "id": "http://example.org/iiif/book1-page1", + "type": "ImageService3", + "profile": "level2" + } + }, + "selector": { + "type": "ImageApiSelector", + "rotation": "90" + } + } + ], + "target": [ + { + "id": "http://example.org/iiif/book1/canvas/p1#xywh=50,50,320,240", + "type": "Canvas" + } + ] +} + +``` + @@ -2568,11 +2588,7 @@ The HTTP server _MUST_ follow the [CORS requirements][org-w3c-cors] to enable br Responses _SHOULD_ be compressed by the server as there are significant performance gains to be made for very repetitive data structures. -## Authentication - -It may be necessary to restrict access to the descriptions made available via the Presentation API. As the primary means of interaction with the descriptions is by web browsers using XmlHttpRequests across domains, there are some considerations regarding the most appropriate methods for authenticating users and authorizing their access. The approach taken is described in the [Authentication][iiif-auth] specification, and requires requesting a token to add to the requests to identify the user. This token might also be used for other requests defined by other APIs. -It is possible to include Image API service descriptions within the Manifest, and within those it is also possible to include links to the Authentication API's services that are needed to interact with the image content. The first time an Authentication API service is included within a Manifest, it _MUST_ be the complete description. Subsequent references _SHOULD_ be just the URI of the service, and clients are expected to look up the details from the full description by matching the URI. Clients _MUST_ anticipate situations where the Authentication service description in the Manifest is out of date: the source of truth is the Image Information document, or other system that references the Authentication API services. # Accessibility diff --git a/source/presentation/4.0/model.md b/source/presentation/4.0/model.md index 70c8d35b9..06f8415d7 100644 --- a/source/presentation/4.0/model.md +++ b/source/presentation/4.0/model.md @@ -653,6 +653,7 @@ A IIIF Image API Selector _MAY_ have the following properties: [id](#id), [regio } ``` +TODO The ImageApiSelector can be used on a static image, you don't need an Image Service. ### Range {: #Range} diff --git a/source/presentation/4.0/scratch.md b/source/presentation/4.0/scratch.md index cbb79d763..59e3b67e6 100644 --- a/source/presentation/4.0/scratch.md +++ b/source/presentation/4.0/scratch.md @@ -196,6 +196,26 @@ What to do about activating annos in the introduced content? +## Chains of activation + +Chaining together activating annotations can then allow the implementation of, at least: + +* Specific camera position to look at an Annotation +* Multi-step linear stories +* Animations, including as part of stories without disrupting the flow, and looping animations (they activate themselves) +* Interactive components such as light switches (enable/disable a light), jukeboxes (enable/disable Audio Emitter) + + +## Storytelling example + +* Something really cool that brings a lot of things together! + + +# Auth of Presentation API resources + +It may be necessary to restrict access to the descriptions made available via the Presentation API. As the primary means of interaction with the descriptions is by web browsers using XmlHttpRequests across domains, there are some considerations regarding the most appropriate methods for authenticating users and authorizing their access. The approach taken is described in the [Authentication][iiif-auth] specification, and requires requesting a token to add to the requests to identify the user. This token might also be used for other requests defined by other APIs. + + _removed content state use case:_