diff --git a/README.md b/README.md
index aa5a9fb..fbd3eac 100644
--- a/README.md
+++ b/README.md
@@ -11,8 +11,6 @@ Keep in mind that this is still just a prototype and that I'm not a frontend dev
## TO-DOs
- Create a calibration page to allow users to calibrate the position and orientation of their camera, and to configure their chroma key (green screen).
-
- - Allow the Mixed Reality Capture session to be initialized with a JSON file with a saved calibration.
## How to test the example
@@ -87,3 +85,40 @@ renderer.render( scene, camera );
mixedRealityCapture.render( renderer.xr, scene );
```
+
+Alternatively, you can instantiate the calibration with a JSON provided by the user:
+
+```javascript
+
+// ...
+
+const json = `
+{
+ "schemaVersion": 1,
+ "camera": {
+ "width": 1280,
+ "height": 720,
+ "fov": 38,
+ "position": [0, 1.5, 0],
+ "orientation": [0, 0, 0, 1]
+ },
+ "chromaKey": {
+ "color": [0, 1, 0],
+ "similarity": 0.25,
+ "smoothness": 0
+ },
+ "delay": 4
+}
+`;
+
+const calibrationData = JSON.parse( json );
+
+const calibration = MRC.Calibration.fromData( calibrationData );
+
+// ...
+
+mixedRealityCapture = new MRC.MixedRealityCapture( calibration );
+
+// ...
+
+```
diff --git a/examples/calibration/calibration-input.js b/examples/calibration/calibration-input.js
new file mode 100644
index 0000000..c867631
--- /dev/null
+++ b/examples/calibration/calibration-input.js
@@ -0,0 +1,134 @@
+import { Calibration } from 'reality-mixer';
+
+const exampleCalibrationJSON = `
+{
+ "schemaVersion": 1,
+ "camera": {
+ "width": 1920,
+ "height": 1080,
+ "fov": 38,
+ "position": [0, 1.5, 0],
+ "orientation": [0, 0, 0, 1]
+ },
+ "chromaKey": {
+ "color": [0, 1, 0],
+ "similarity": 0.25,
+ "smoothness": 0
+ },
+ "delay": 4
+}
+`
+
+function CalibrationInput(
+ onCompleted,
+ initialCalibrationJSON = null
+) {
+ let initialJSON;
+
+ if (initialCalibrationJSON == null) {
+ let persistedJSON = localStorage.getItem("calibration-v1");
+
+ if (persistedJSON == null) {
+ initialJSON = exampleCalibrationJSON;
+ } else {
+ initialJSON = persistedJSON;
+ }
+ } else {
+ initialJSON = initialCalibrationJSON;
+ }
+
+ let popupDiv = document.createElement("div");
+
+ popupDiv.style = `
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background-color: #303841;
+ border-radius: 4px;
+ font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
+ color: #EFEFEF;
+ overflow: hidden;
+ `
+
+ let titleDiv = document.createElement("div");
+
+ titleDiv.style = "background-color: #50565E; padding: 8px";
+ titleDiv.innerText = "Paste or drag and drop your Mixed Reality calibration below:"
+
+ let resultDiv = document.createElement("div");
+
+ resultDiv.style = "padding: 8px; white-space: pre-wrap; background-color: green;";
+
+ let editor = document.createElement("textarea");
+
+ editor.style = `
+ font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
+ border: none;
+ box-shadow: none;
+ outline: none;
+ background-color: #303841;
+ color: #EFEFEF;
+ resize: none;
+ margin: 0px;
+ `
+
+ editor.rows = 20;
+ editor.cols = 80;
+ editor.spellcheck = false;
+ editor.value = initialJSON;
+
+ function validateCalibration() {
+ const maybeJson = editor.value;
+
+ try {
+ const json = JSON.parse(maybeJson);
+ const calibration = Calibration.fromData(json);
+
+ resultDiv.style.backgroundColor = "green";
+ resultDiv.innerHTML = "";
+
+ let link = document.createElement("a");
+ link.innerText = "Click here to continue...";
+ link.href = "#";
+ link.style.color = "#EFEFEF";
+
+ link.onclick = function() {
+ localStorage.setItem("calibration-v1", maybeJson);
+ onCompleted(popupDiv, calibration);
+ return false;
+ }
+
+ resultDiv.appendChild(link);
+ } catch (error) {
+ resultDiv.innerText = error;
+ resultDiv.style.backgroundColor = "red";
+ }
+ }
+
+ editor.oninput = validateCalibration;
+ validateCalibration();
+
+ popupDiv.appendChild(titleDiv);
+ popupDiv.appendChild(editor);
+ popupDiv.appendChild(resultDiv);
+
+ popupDiv.ondrop = function(e) {
+ e.preventDefault();
+
+ let file = e.dataTransfer.files[0],
+ reader = new FileReader();
+
+ reader.onload = function(event) {
+ editor.value = event.target.result;
+ validateCalibration();
+ };
+
+ reader.readAsText(file);
+ return false;
+ }
+
+ return popupDiv;
+}
+
+export { CalibrationInput };
diff --git a/examples/calibration_input.html b/examples/calibration_input.html
new file mode 100644
index 0000000..d177202
--- /dev/null
+++ b/examples/calibration_input.html
@@ -0,0 +1,38 @@
+
+
+
+
+ Calibration input
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/webxr_vr_ballshooter.html b/examples/webxr_vr_ballshooter.html
index 2058b13..c6480c8 100644
--- a/examples/webxr_vr_ballshooter.html
+++ b/examples/webxr_vr_ballshooter.html
@@ -24,6 +24,7 @@
import * as THREE from 'three';
import * as MRC from 'reality-mixer';
+ import { CalibrationInput } from './calibration/calibration-input.js';
import { BoxLineGeometry } from '../node_modules/three/examples/jsm/geometries/BoxLineGeometry.js';
import { VRButton } from '../node_modules/three/examples/jsm/webxr/VRButton.js';
@@ -44,10 +45,23 @@
const clock = new THREE.Clock();
- init();
- animate();
+ // Mixed Reality Calibration Input
- function init() {
+ function onCalibrationInput(editor, calibration) {
+ editor.parentNode.removeChild(editor);
+ console.log(calibration);
+
+ init(calibration);
+ animate();
+ }
+
+ const calibrationInput = CalibrationInput(onCalibrationInput);
+
+ document.body.appendChild(calibrationInput);
+
+ //
+
+ function init(calibration) {
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x505050 );
@@ -89,11 +103,6 @@
// Mixed Reality Capture
- const cameraCalibration = new MRC.CameraCalibration();
- const chromaKey = new MRC.ChromaKey();
- const delayInFrames = 5;
- const calibration = new MRC.Calibration( cameraCalibration, chromaKey, delayInFrames );
-
mixedRealityCapture = new MRC.MixedRealityCapture( calibration );
//
diff --git a/package-lock.json b/package-lock.json
index e437975..ffdfe7a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,28 +1,435 @@
{
"name": "reality-mixer",
- "version": "1.0.0",
+ "version": "1.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "reality-mixer",
- "version": "1.0.0",
+ "version": "1.1.0",
"license": "MIT",
"dependencies": {
"three": "^0.140.0"
+ },
+ "devDependencies": {
+ "ajv-cli": "git@github.com:willfarrell/ajv-cli.git#15c4f0153e25da2737d673f0c027e3ab0f5b1ec8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.11.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-cli": {
+ "version": "5.0.0",
+ "resolved": "git+ssh://git@github.com/willfarrell/ajv-cli.git#15c4f0153e25da2737d673f0c027e3ab0f5b1ec8",
+ "integrity": "sha512-wbvOdDG+J6thaGIE13mGG8q9p5pIkhU7yGj1uY3dGlkxAXnUaN3zb/QYzv3hkPajM+TuvA3EZx+1ZWuWnn+ESA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0",
+ "fast-json-patch": "^2.0.0",
+ "glob": "^7.1.0",
+ "js-yaml": "^3.14.0",
+ "json-schema-migrate": "^2.0.0",
+ "json5": "^2.1.3",
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "ajv": "dist/index.js"
+ },
+ "peerDependencies": {
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-patch": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/fast-json-patch/node_modules/fast-deep-equal": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/glob": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-schema-migrate": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
}
},
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/three": {
"version": "0.140.0",
- "resolved": "https://registry.npmjs.org/three/-/three-0.140.0.tgz",
- "integrity": "sha512-jcHjbnYspPLDdsDQChmzyAoZ5KhJbgFk6pNGlAIc9fQMvsfPGjF5H9glrngqvb2CR/qXcClMyp5PYdF996lldA=="
+ "license": "MIT"
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "ISC"
}
},
"dependencies": {
+ "ajv": {
+ "version": "8.11.0",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-cli": {
+ "version": "git+ssh://git@github.com/willfarrell/ajv-cli.git#15c4f0153e25da2737d673f0c027e3ab0f5b1ec8",
+ "integrity": "sha512-wbvOdDG+J6thaGIE13mGG8q9p5pIkhU7yGj1uY3dGlkxAXnUaN3zb/QYzv3hkPajM+TuvA3EZx+1ZWuWnn+ESA==",
+ "dev": true,
+ "from": "ajv-cli@git@github.com:willfarrell/ajv-cli.git#15c4f0153e25da2737d673f0c027e3ab0f5b1ec8",
+ "requires": {
+ "ajv": "^8.0.0",
+ "fast-json-patch": "^2.0.0",
+ "glob": "^7.1.0",
+ "js-yaml": "^3.14.0",
+ "json-schema-migrate": "^2.0.0",
+ "json5": "^2.1.3",
+ "minimist": "^1.2.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "dev": true
+ },
+ "fast-json-patch": {
+ "version": "2.2.1",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1"
+ },
+ "dependencies": {
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "dev": true
+ }
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.2.0",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-schema-migrate": {
+ "version": "2.0.0",
+ "dev": true,
+ "requires": {
+ "ajv": "^8.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.2.1",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.6",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "dev": true
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "dev": true
+ },
"three": {
- "version": "0.140.0",
- "resolved": "https://registry.npmjs.org/three/-/three-0.140.0.tgz",
- "integrity": "sha512-jcHjbnYspPLDdsDQChmzyAoZ5KhJbgFk6pNGlAIc9fQMvsfPGjF5H9glrngqvb2CR/qXcClMyp5PYdF996lldA=="
+ "version": "0.140.0"
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "dev": true
}
}
}
diff --git a/package.json b/package.json
index 1b53390..9a90aa2 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,11 @@
{
"name": "reality-mixer",
- "version": "1.0.0",
+ "version": "1.1.0",
"description": "Mixed Reality Capture for WebXR and Three.js",
"type": "module",
"main": "src/mrc.js",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "compile-schema": "ajv compile -s schemas/calibration-schema.json -o src/validate-schema.js --code-esm"
},
"repository": {
"type": "git",
@@ -29,5 +29,8 @@
"homepage": "https://github.com/fabio914/reality-mixer-js#readme",
"dependencies": {
"three": "^0.140.0"
+ },
+ "devDependencies": {
+ "ajv-cli": "git@github.com:willfarrell/ajv-cli.git#15c4f0153e25da2737d673f0c027e3ab0f5b1ec8"
}
}
diff --git a/schemas/calibration-schema.json b/schemas/calibration-schema.json
new file mode 100644
index 0000000..cc52661
--- /dev/null
+++ b/schemas/calibration-schema.json
@@ -0,0 +1,91 @@
+{
+ "type": "object",
+ "properties": {
+ "schemaVersion": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 1
+ },
+ "camera": {
+ "type": "object",
+ "properties": {
+ "width": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "height": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "fov": {
+ "type": "number",
+ "minimum": 1
+ },
+ "position": {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": {
+ "type": "number"
+ }
+ },
+ "orientation": {
+ "type": "array",
+ "minItems": 4,
+ "maxItems": 4,
+ "items": {
+ "type": "number"
+ }
+ }
+ },
+ "required": [
+ "width",
+ "height",
+ "fov",
+ "position",
+ "orientation"
+ ]
+ },
+ "chromaKey": {
+ "type": "object",
+ "properties": {
+ "color": {
+ "type": "array",
+ "minItems": 3,
+ "maxItems": 3,
+ "items": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 1
+ }
+ },
+ "similarity": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 1
+ },
+ "smoothness": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 1
+ }
+ },
+ "required": [
+ "color",
+ "similarity",
+ "smoothness"
+ ]
+ },
+ "delay": {
+ "type": "integer",
+ "minimum": 0
+ }
+ },
+ "required": [
+ "schemaVersion",
+ "camera",
+ "chromaKey",
+ "delay"
+ ],
+ "additionalProperties": false
+}
diff --git a/src/mrc.js b/src/mrc.js
index 9001a7f..aa0976c 100644
--- a/src/mrc.js
+++ b/src/mrc.js
@@ -1,4 +1,5 @@
import * as THREE from 'three';
+import validate from './validate-schema.js';
class CameraCalibration {
width;
@@ -52,6 +53,36 @@ class Calibration {
this.chromaKey = chromaKey;
this.delayInFrames = Math.max(0, delayInFrames);
}
+
+ static fromData(data) {
+ // We're not checking if the orientation is a valid unit quaternion
+
+ if (!validate(data)) {
+ throw JSON.stringify(validate.errors, null, 2);
+ }
+
+ const cameraCalibration = new CameraCalibration(
+ data.camera.width,
+ data.camera.height,
+ data.camera.fov,
+ data.camera.position,
+ data.camera.orientation
+ )
+
+ const chromaKey = new ChromaKey(
+ data.chromaKey.color,
+ data.chromaKey.similarity,
+ data.chromaKey.smoothness
+ )
+
+ const calibration = new Calibration(
+ cameraCalibration,
+ chromaKey,
+ data.delay
+ )
+
+ return calibration;
+ }
}
class MixedRealityCapture {
diff --git a/src/validate-schema.js b/src/validate-schema.js
new file mode 100644
index 0000000..323630c
--- /dev/null
+++ b/src/validate-schema.js
@@ -0,0 +1 @@
+"use strict";export const validate = validate20;export default validate20;const schema22 = {"type":"object","properties":{"schemaVersion":{"type":"integer","minimum":1,"maximum":1},"camera":{"type":"object","properties":{"width":{"type":"integer","minimum":1},"height":{"type":"integer","minimum":1},"fov":{"type":"number","minimum":1},"position":{"type":"array","minItems":3,"maxItems":3,"items":{"type":"number"}},"orientation":{"type":"array","minItems":4,"maxItems":4,"items":{"type":"number"}}},"required":["width","height","fov","position","orientation"]},"chromaKey":{"type":"object","properties":{"color":{"type":"array","minItems":3,"maxItems":3,"items":{"type":"number","minimum":0,"maximum":1}},"similarity":{"type":"number","minimum":0,"maximum":1},"smoothness":{"type":"number","minimum":0,"maximum":1}},"required":["color","similarity","smoothness"]},"delay":{"type":"integer","minimum":0}},"required":["schemaVersion","camera","chromaKey","delay"],"additionalProperties":false};function validate20(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if(((((data.schemaVersion === undefined) && (missing0 = "schemaVersion")) || ((data.camera === undefined) && (missing0 = "camera"))) || ((data.chromaKey === undefined) && (missing0 = "chromaKey"))) || ((data.delay === undefined) && (missing0 = "delay"))){validate20.errors = [{instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: missing0},message:"must have required property '"+missing0+"'"}];return false;}else {const _errs1 = errors;for(const key0 in data){if(!((((key0 === "schemaVersion") || (key0 === "camera")) || (key0 === "chromaKey")) || (key0 === "delay"))){validate20.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.schemaVersion !== undefined){let data0 = data.schemaVersion;const _errs2 = errors;if(!(((typeof data0 == "number") && (!(data0 % 1) && !isNaN(data0))) && (isFinite(data0)))){validate20.errors = [{instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/type",keyword:"type",params:{type: "integer"},message:"must be integer"}];return false;}if(errors === _errs2){if((typeof data0 == "number") && (isFinite(data0))){if(data0 > 1 || isNaN(data0)){validate20.errors = [{instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/maximum",keyword:"maximum",params:{comparison: "<=", limit: 1},message:"must be <= 1"}];return false;}else {if(data0 < 1 || isNaN(data0)){validate20.errors = [{instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"}];return false;}}}}var valid0 = _errs2 === errors;}else {var valid0 = true;}if(valid0){if(data.camera !== undefined){let data1 = data.camera;const _errs4 = errors;if(errors === _errs4){if(data1 && typeof data1 == "object" && !Array.isArray(data1)){let missing1;if((((((data1.width === undefined) && (missing1 = "width")) || ((data1.height === undefined) && (missing1 = "height"))) || ((data1.fov === undefined) && (missing1 = "fov"))) || ((data1.position === undefined) && (missing1 = "position"))) || ((data1.orientation === undefined) && (missing1 = "orientation"))){validate20.errors = [{instancePath:instancePath+"/camera",schemaPath:"#/properties/camera/required",keyword:"required",params:{missingProperty: missing1},message:"must have required property '"+missing1+"'"}];return false;}else {if(data1.width !== undefined){let data2 = data1.width;const _errs6 = errors;if(!(((typeof data2 == "number") && (!(data2 % 1) && !isNaN(data2))) && (isFinite(data2)))){validate20.errors = [{instancePath:instancePath+"/camera/width",schemaPath:"#/properties/camera/properties/width/type",keyword:"type",params:{type: "integer"},message:"must be integer"}];return false;}if(errors === _errs6){if((typeof data2 == "number") && (isFinite(data2))){if(data2 < 1 || isNaN(data2)){validate20.errors = [{instancePath:instancePath+"/camera/width",schemaPath:"#/properties/camera/properties/width/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"}];return false;}}}var valid1 = _errs6 === errors;}else {var valid1 = true;}if(valid1){if(data1.height !== undefined){let data3 = data1.height;const _errs8 = errors;if(!(((typeof data3 == "number") && (!(data3 % 1) && !isNaN(data3))) && (isFinite(data3)))){validate20.errors = [{instancePath:instancePath+"/camera/height",schemaPath:"#/properties/camera/properties/height/type",keyword:"type",params:{type: "integer"},message:"must be integer"}];return false;}if(errors === _errs8){if((typeof data3 == "number") && (isFinite(data3))){if(data3 < 1 || isNaN(data3)){validate20.errors = [{instancePath:instancePath+"/camera/height",schemaPath:"#/properties/camera/properties/height/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"}];return false;}}}var valid1 = _errs8 === errors;}else {var valid1 = true;}if(valid1){if(data1.fov !== undefined){let data4 = data1.fov;const _errs10 = errors;if(errors === _errs10){if((typeof data4 == "number") && (isFinite(data4))){if(data4 < 1 || isNaN(data4)){validate20.errors = [{instancePath:instancePath+"/camera/fov",schemaPath:"#/properties/camera/properties/fov/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"}];return false;}}else {validate20.errors = [{instancePath:instancePath+"/camera/fov",schemaPath:"#/properties/camera/properties/fov/type",keyword:"type",params:{type: "number"},message:"must be number"}];return false;}}var valid1 = _errs10 === errors;}else {var valid1 = true;}if(valid1){if(data1.position !== undefined){let data5 = data1.position;const _errs12 = errors;if(errors === _errs12){if(Array.isArray(data5)){if(data5.length > 3){validate20.errors = [{instancePath:instancePath+"/camera/position",schemaPath:"#/properties/camera/properties/position/maxItems",keyword:"maxItems",params:{limit: 3},message:"must NOT have more than 3 items"}];return false;}else {if(data5.length < 3){validate20.errors = [{instancePath:instancePath+"/camera/position",schemaPath:"#/properties/camera/properties/position/minItems",keyword:"minItems",params:{limit: 3},message:"must NOT have fewer than 3 items"}];return false;}else {var valid2 = true;const len0 = data5.length;for(let i0=0; i0 4){validate20.errors = [{instancePath:instancePath+"/camera/orientation",schemaPath:"#/properties/camera/properties/orientation/maxItems",keyword:"maxItems",params:{limit: 4},message:"must NOT have more than 4 items"}];return false;}else {if(data7.length < 4){validate20.errors = [{instancePath:instancePath+"/camera/orientation",schemaPath:"#/properties/camera/properties/orientation/minItems",keyword:"minItems",params:{limit: 4},message:"must NOT have fewer than 4 items"}];return false;}else {var valid3 = true;const len1 = data7.length;for(let i1=0; i1 3){validate20.errors = [{instancePath:instancePath+"/chromaKey/color",schemaPath:"#/properties/chromaKey/properties/color/maxItems",keyword:"maxItems",params:{limit: 3},message:"must NOT have more than 3 items"}];return false;}else {if(data10.length < 3){validate20.errors = [{instancePath:instancePath+"/chromaKey/color",schemaPath:"#/properties/chromaKey/properties/color/minItems",keyword:"minItems",params:{limit: 3},message:"must NOT have fewer than 3 items"}];return false;}else {var valid5 = true;const len2 = data10.length;for(let i2=0; i2 1 || isNaN(data11)){validate20.errors = [{instancePath:instancePath+"/chromaKey/color/" + i2,schemaPath:"#/properties/chromaKey/properties/color/items/maximum",keyword:"maximum",params:{comparison: "<=", limit: 1},message:"must be <= 1"}];return false;}else {if(data11 < 0 || isNaN(data11)){validate20.errors = [{instancePath:instancePath+"/chromaKey/color/" + i2,schemaPath:"#/properties/chromaKey/properties/color/items/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"}];return false;}}}else {validate20.errors = [{instancePath:instancePath+"/chromaKey/color/" + i2,schemaPath:"#/properties/chromaKey/properties/color/items/type",keyword:"type",params:{type: "number"},message:"must be number"}];return false;}}var valid5 = _errs24 === errors;if(!valid5){break;}}}}}else {validate20.errors = [{instancePath:instancePath+"/chromaKey/color",schemaPath:"#/properties/chromaKey/properties/color/type",keyword:"type",params:{type: "array"},message:"must be array"}];return false;}}var valid4 = _errs22 === errors;}else {var valid4 = true;}if(valid4){if(data9.similarity !== undefined){let data12 = data9.similarity;const _errs26 = errors;if(errors === _errs26){if((typeof data12 == "number") && (isFinite(data12))){if(data12 > 1 || isNaN(data12)){validate20.errors = [{instancePath:instancePath+"/chromaKey/similarity",schemaPath:"#/properties/chromaKey/properties/similarity/maximum",keyword:"maximum",params:{comparison: "<=", limit: 1},message:"must be <= 1"}];return false;}else {if(data12 < 0 || isNaN(data12)){validate20.errors = [{instancePath:instancePath+"/chromaKey/similarity",schemaPath:"#/properties/chromaKey/properties/similarity/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"}];return false;}}}else {validate20.errors = [{instancePath:instancePath+"/chromaKey/similarity",schemaPath:"#/properties/chromaKey/properties/similarity/type",keyword:"type",params:{type: "number"},message:"must be number"}];return false;}}var valid4 = _errs26 === errors;}else {var valid4 = true;}if(valid4){if(data9.smoothness !== undefined){let data13 = data9.smoothness;const _errs28 = errors;if(errors === _errs28){if((typeof data13 == "number") && (isFinite(data13))){if(data13 > 1 || isNaN(data13)){validate20.errors = [{instancePath:instancePath+"/chromaKey/smoothness",schemaPath:"#/properties/chromaKey/properties/smoothness/maximum",keyword:"maximum",params:{comparison: "<=", limit: 1},message:"must be <= 1"}];return false;}else {if(data13 < 0 || isNaN(data13)){validate20.errors = [{instancePath:instancePath+"/chromaKey/smoothness",schemaPath:"#/properties/chromaKey/properties/smoothness/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"}];return false;}}}else {validate20.errors = [{instancePath:instancePath+"/chromaKey/smoothness",schemaPath:"#/properties/chromaKey/properties/smoothness/type",keyword:"type",params:{type: "number"},message:"must be number"}];return false;}}var valid4 = _errs28 === errors;}else {var valid4 = true;}}}}}else {validate20.errors = [{instancePath:instancePath+"/chromaKey",schemaPath:"#/properties/chromaKey/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid0 = _errs20 === errors;}else {var valid0 = true;}if(valid0){if(data.delay !== undefined){let data14 = data.delay;const _errs30 = errors;if(!(((typeof data14 == "number") && (!(data14 % 1) && !isNaN(data14))) && (isFinite(data14)))){validate20.errors = [{instancePath:instancePath+"/delay",schemaPath:"#/properties/delay/type",keyword:"type",params:{type: "integer"},message:"must be integer"}];return false;}if(errors === _errs30){if((typeof data14 == "number") && (isFinite(data14))){if(data14 < 0 || isNaN(data14)){validate20.errors = [{instancePath:instancePath+"/delay",schemaPath:"#/properties/delay/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"}];return false;}}}var valid0 = _errs30 === errors;}else {var valid0 = true;}}}}}}}else {validate20.errors = [{instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}validate20.errors = vErrors;return errors === 0;}
\ No newline at end of file