From 279a85748207336bb578218de4299d6b26f1d497 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Mon, 16 Apr 2018 11:41:18 +0200 Subject: [PATCH] refactor(pointcloud): simplify SSE computation for pointcloud Project average point spacing on screen and compare to sse threshold. It's a simplification of the approach proposed in #674. This allows to drop a dependency (convexhull) and brings the SSE compuation for pointcloud closer to what is done for other geometries. --- package-lock.json | 963 +------------------------ package.json | 1 - src/Process/PointCloudProcessing.js | 241 +++---- src/Provider/PointCloudProvider.js | 5 +- src/Renderer/PointsMaterial.js | 1 - src/Renderer/Shader/PointsVS.glsl | 1 - test/pointcloudprocessing_unit_test.js | 9 +- utils/debug/PointCloudDebug.js | 4 +- 8 files changed, 97 insertions(+), 1128 deletions(-) diff --git a/package-lock.json b/package-lock.json index 43bdae83e0..c94a23a1ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1601,7 +1601,6 @@ "requires": { "anymatch": "1.3.0", "async-each": "1.0.1", - "fsevents": "1.1.3", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -3045,910 +3044,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", - "dev": true, - "optional": true, - "requires": { - "nan": "2.8.0", - "node-pre-gyp": "0.6.39" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true, - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "bundled": true, - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - } - } - }, "function-bind": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", @@ -5617,14 +4712,6 @@ "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=", "dev": true }, - "monotone-convex-hull-2d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz", - "integrity": "sha1-R/Xa6t88Sv03dkuqGqh4ekDu4Iw=", - "requires": { - "robust-orientation": "1.1.3" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5653,13 +4740,6 @@ "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", "dev": true }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", - "dev": true, - "optional": true - }, "nanomatch": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", @@ -5954,7 +5034,7 @@ }, "onetime": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, @@ -6753,36 +5833,6 @@ "inherits": "2.0.3" } }, - "robust-orientation": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/robust-orientation/-/robust-orientation-1.1.3.tgz", - "integrity": "sha1-2v9bANO+TmByLw6cAVbvln8cIEk=", - "requires": { - "robust-scale": "1.0.2", - "robust-subtract": "1.0.0", - "robust-sum": "1.0.0", - "two-product": "1.0.2" - } - }, - "robust-scale": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/robust-scale/-/robust-scale-1.0.2.tgz", - "integrity": "sha1-d1Ey7QlULQKOWLLMecBikLz3jDI=", - "requires": { - "two-product": "1.0.2", - "two-sum": "1.0.0" - } - }, - "robust-subtract": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/robust-subtract/-/robust-subtract-1.0.0.tgz", - "integrity": "sha1-4LFk4e2LpOOl3aRaEgODSNvtPpo=" - }, - "robust-sum": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz", - "integrity": "sha1-FmRuUlKStNJdgnV6KGlV4Lv6U9k=" - }, "run-async": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", @@ -7796,16 +6846,6 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, - "two-product": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/two-product/-/two-product-1.0.2.tgz", - "integrity": "sha1-Z9ldSyV6kh4stL16+VEfkIhSLqo=" - }, - "two-sum": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/two-sum/-/two-sum-1.0.0.tgz", - "integrity": "sha1-MdPzIjnk9zHsqd+RVeKyl/AIq2Q=" - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -8674,7 +7714,6 @@ "anymatch": "2.0.0", "async-each": "1.0.1", "braces": "2.3.1", - "fsevents": "1.1.3", "glob-parent": "3.1.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", diff --git a/package.json b/package.json index 3fdebb9de0..affcc0c842 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "flatbush": "^1.3.0", "js-priority-queue": "^0.1.5", "jszip": "^3.1.3", - "monotone-convex-hull-2d": "^1.0.1", "text-encoding-utf-8": "^1.0.1", "togeojson": "^0.16.0", "url-polyfill": "^1.0.8", diff --git a/src/Process/PointCloudProcessing.js b/src/Process/PointCloudProcessing.js index 7cefacd6ba..e56c846a8e 100644 --- a/src/Process/PointCloudProcessing.js +++ b/src/Process/PointCloudProcessing.js @@ -1,5 +1,4 @@ import * as THREE from 'three'; -import convexHull from 'monotone-convex-hull-2d'; import CancelledCommandException from '../Core/Scheduler/CancelledCommandException'; // Draw a cube with lines (12 lines). @@ -35,59 +34,6 @@ function cube(size) { return geometry; } -// TODO: move this function to Camera, as soon as it's good enough (see https://github.com/iTowns/itowns/pull/381#pullrequestreview-49107682) -const temp = { - points: [ - new THREE.Vector3(), - new THREE.Vector3(), - new THREE.Vector3(), - new THREE.Vector3(), - new THREE.Vector3(), - new THREE.Vector3(), - new THREE.Vector3(), - new THREE.Vector3(), - ], - box3: new THREE.Box3(), - matrix4: new THREE.Matrix4(), -}; -function box3SurfaceOnScreen(camera, box3d, matrixWorld) { - if (box3d.isEmpty()) { - return 0; - } - - temp.box3.copy(box3d); - if (matrixWorld) { - temp.matrix4.multiplyMatrices(camera._viewMatrix, matrixWorld); - } else { - temp.matrix4.copy(camera._viewMatrix); - } - - // copy pasted / adapted from Box3.applyMatrix4 - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - temp.points[0].set(temp.box3.min.x, temp.box3.min.y, temp.box3.min.z).applyMatrix4(temp.matrix4); // 000 - temp.points[1].set(temp.box3.min.x, temp.box3.min.y, temp.box3.max.z).applyMatrix4(temp.matrix4); // 001 - temp.points[2].set(temp.box3.min.x, temp.box3.max.y, temp.box3.min.z).applyMatrix4(temp.matrix4); // 010 - temp.points[3].set(temp.box3.min.x, temp.box3.max.y, temp.box3.max.z).applyMatrix4(temp.matrix4); // 011 - temp.points[4].set(temp.box3.max.x, temp.box3.min.y, temp.box3.min.z).applyMatrix4(temp.matrix4); // 100 - temp.points[5].set(temp.box3.max.x, temp.box3.min.y, temp.box3.max.z).applyMatrix4(temp.matrix4); // 101 - temp.points[6].set(temp.box3.max.x, temp.box3.max.y, temp.box3.min.z).applyMatrix4(temp.matrix4); // 110 - temp.points[7].set(temp.box3.max.x, temp.box3.max.y, temp.box3.max.z).applyMatrix4(temp.matrix4); // 111 - - for (const pt of temp.points) { - // translate/scale to [0, width]x[0, height] - pt.x = camera.width * (pt.x + 1) * 0.5; - pt.y = camera.height * (1 - pt.y) * 0.5; - pt.z = 0; - } - - const indices = convexHull(temp.points.map(v => [v.x, v.y])); - const contour = indices.map(i => temp.points[i]); - - const area = THREE.ShapeUtils.area(contour); - - return Math.abs(area); -} - function initBoundingBox(elt, layer) { const size = elt.tightbbox.getSize(); elt.obj.boxHelper = new THREE.LineSegments( @@ -106,41 +52,18 @@ function initBoundingBox(elt, layer) { elt.obj.boxHelper.updateMatrixWorld(); } -function shouldDisplayNode(context, layer, elt) { - let shouldBeLoaded = 0; - - if (layer.octreeDepthLimit >= 0 && layer.octreeDepthLimit < elt.name.length) { - return { shouldBeLoaded, surfaceOnScreen: 0 }; +function computeScreenSpaceError(context, layer, elt, distance) { + if (distance <= 0) { + return layer.sseThreshold; } - - const numPoints = elt.numPoints; - - const cl = (elt.tightbbox ? elt.tightbbox : elt.bbox); - - const visible = context.camera.isBox3Visible(cl, layer.object3d.matrixWorld); - const surfaceOnScreen = 0; - - if (visible) { - if (cl.containsPoint(context.camera.camera3D.position)) { - shouldBeLoaded = 1; - } else { - const surfaceOnScreen = box3SurfaceOnScreen(context.camera, cl, layer.object3d.matrixWorld); - - // no point indicates shallow hierarchy, so we definitely want to load its children - if (numPoints == 0) { - shouldBeLoaded = 1; - } else { - const count = layer.overdraw * (surfaceOnScreen / Math.pow(layer.pointSize, 2)); - shouldBeLoaded = Math.min(count / numPoints, 1); - } - - elt.surfaceOnScreen = surfaceOnScreen; - } - } else { - shouldBeLoaded = -1; - } - - return { shouldBeLoaded, surfaceOnScreen }; + const pointSpacing = layer.metadata.spacing / Math.pow(2, elt.name.length); + // Estimate the onscreen distance between 2 points + const onScreenSpacing = context.camera.preSSE * pointSpacing / distance; + // [ P1 ]--------------[ P2 ] + // <---------------------> = pointsSpacing (in world coordinates) + // ~ onScreenSpacing (in pixels) + // <------> = layer.pointSize (in pixels) + return Math.max(0.0, onScreenSpacing - layer.pointSize); } function markForDeletion(elt) { @@ -155,7 +78,7 @@ function markForDeletion(elt) { if (!elt.notVisibleSince) { elt.notVisibleSince = Date.now(); - elt.shouldBeLoaded = -1; + elt.sse = -1; } for (const child of elt.children) { markForDeletion(child); @@ -169,6 +92,11 @@ export default { return []; } + context.camera.preSSE = + context.camera.height / + (2 * Math.tan(THREE.Math.degToRad(context.camera.camera3D.fov) * 0.5)); + + if (changeSources.has(undefined) || changeSources.size == 0) { return [layer.root]; } @@ -212,74 +140,85 @@ export default { }, update(context, layer, elt) { - const { shouldBeLoaded } = shouldDisplayNode(context, layer, elt); + elt.visible = false; - elt.shouldBeLoaded = shouldBeLoaded; + if (layer.octreeDepthLimit >= 0 && layer.octreeDepthLimit < elt.name.length) { + markForDeletion(elt); + return; + } - if (shouldBeLoaded > 0) { - elt.notVisibleSince = undefined; + // pick the best bounding box + const bbox = (elt.tightbbox ? elt.tightbbox : elt.bbox); + elt.visible = context.camera.isBox3Visible(bbox, layer.object3d.matrixWorld); + if (!elt.visible) { + markForDeletion(elt); + return; + } - // only load geometry if this elements has points - if (elt.numPoints > 0) { - if (elt.obj) { - elt.obj.material.visible = true; - if (__DEBUG__) { - elt.obj.material.uniforms.density.value = elt.density; + elt.notVisibleSince = undefined; - if (layer.bboxes.visible) { - if (!elt.obj.boxHelper) { - initBoundingBox(elt, layer); - } - elt.obj.boxHelper.visible = true; - elt.obj.boxHelper.material.color.r = 1 - shouldBeLoaded; - elt.obj.boxHelper.material.color.g = shouldBeLoaded; + // only load geometry if this elements has points + if (elt.numPoints > 0) { + if (elt.obj) { + elt.obj.material.visible = true; + elt.obj.material.uniforms.size.value = layer.pointSize; + + if (__DEBUG__) { + if (layer.bboxes.visible) { + if (!elt.obj.boxHelper) { + initBoundingBox(elt, layer); } + elt.obj.boxHelper.visible = true; + elt.obj.boxHelper.material.color.r = 1 - elt.sse; + elt.obj.boxHelper.material.color.g = elt.sse; + } + } + } else if (!elt.promise) { + const distance = Math.max(0.001, bbox.distanceToPoint(context.camera.camera3D.position)); + const priority = computeScreenSpaceError(context, layer, elt, distance) / distance; + elt.promise = context.scheduler.execute({ + layer, + requester: elt, + view: context.view, + priority, + redraw: true, + isLeaf: elt.childrenBitField == 0, + earlyDropFunction: cmd => !cmd.requester.visible, + }).then((pts) => { + if (layer.onPointsCreated) { + layer.onPointsCreated(layer, pts); } - elt.obj.material.uniforms.size.value = layer.pointSize; - } else if (!elt.promise) { - // TODO: - // - add command cancelation support - // - rework priority - elt.promise = context.scheduler.execute({ - layer, - requester: elt, - view: context.view, - priority: 1.0 / elt.name.length, // surfaceOnScreen, - redraw: true, - isLeaf: elt.childrenBitField == 0, - earlyDropFunction: cmd => cmd.requester.shouldBeLoaded <= 0, - }).then((pts) => { - if (layer.onPointsCreated) { - layer.onPointsCreated(layer, pts); - } - elt.obj = pts; - // store tightbbox to avoid ping-pong (bbox = larger => visible, tight => invisible) - elt.tightbbox = pts.tightbbox; + elt.obj = pts; + // store tightbbox to avoid ping-pong (bbox = larger => visible, tight => invisible) + elt.tightbbox = pts.tightbbox; - // make sure to add it here, otherwise it might never - // be added nor cleaned - layer.group.add(elt.obj); - elt.obj.updateMatrixWorld(true); + // make sure to add it here, otherwise it might never + // be added nor cleaned + layer.group.add(elt.obj); + elt.obj.updateMatrixWorld(true); - elt.obj.owner = elt; + elt.obj.owner = elt; + elt.promise = null; + }, (err) => { + if (err instanceof CancelledCommandException) { elt.promise = null; - }, (err) => { - if (err instanceof CancelledCommandException) { - elt.promise = null; - } - }); - } + } + }); } - } else { - // not visible / displayed - markForDeletion(elt); } - if (shouldBeLoaded >= 0.9 && elt.children && elt.children.length) { - return elt.children; + if (elt.children && elt.children.length) { + const distance = bbox.distanceToPoint(context.camera.camera3D.position); + elt.sse = computeScreenSpaceError(context, layer, elt, distance) / layer.sseThreshold; + if (elt.sse >= 1) { + return elt.children; + } else { + for (const child of elt.children) { + markForDeletion(child); + } + } } - return undefined; }, postUpdate(context, layer) { @@ -290,17 +229,9 @@ export default { layer.displayedCount = 0; for (const pts of layer.group.children) { if (pts.material.visible) { - if (layer.metadata.customBinFormat) { - const count = Math.floor(pts.owner.shouldBeLoaded * pts.geometry.attributes.position.count); - if (count > 0) { - pts.geometry.setDrawRange(0, count); - layer.displayedCount += count; - } else { - pts.material.visible = false; - } - } else { - layer.displayedCount += pts.geometry.attributes.position.count; - } + const count = pts.geometry.attributes.position.count; + pts.geometry.setDrawRange(0, count); + layer.displayedCount += count; } } @@ -326,7 +257,7 @@ export default { // This format doesn't require points to be evenly distributed, so // we're going to sort the nodes by "importance" (= on screen size) // and display only the first N nodes - layer.group.children.sort((p1, p2) => p2.owner.surfaceOnScreen - p1.owner.surfaceOnScreen); + layer.group.children.sort((p1, p2) => p2.owner.sse - p1.owner.sse); let limitHit = false; layer.displayedCount = 0; diff --git a/src/Provider/PointCloudProvider.js b/src/Provider/PointCloudProvider.js index f88c0400aa..453afe1c81 100644 --- a/src/Provider/PointCloudProvider.js +++ b/src/Provider/PointCloudProvider.js @@ -158,14 +158,15 @@ export default { layer.bboxes = new THREE.Group(); layer.object3d.add(layer.bboxes); layer.bboxes.updateMatrixWorld(); + layer.bboxes.visible = false; } // default options layer.fetchOptions = layer.fetchOptions || {}; layer.octreeDepthLimit = layer.octreeDepthLimit || -1; - layer.pointBudget = layer.pointBudget || 15000000; + layer.pointBudget = layer.pointBudget || 2000000; layer.pointSize = layer.pointSize === 0 || !isNaN(layer.pointSize) ? layer.pointSize : 4; - layer.overdraw = layer.overdraw || 2; + layer.sseThreshold = 1; layer.type = 'geometry'; // default update methods diff --git a/src/Renderer/PointsMaterial.js b/src/Renderer/PointsMaterial.js index a34e6fe1ee..ecc6bf02ce 100644 --- a/src/Renderer/PointsMaterial.js +++ b/src/Renderer/PointsMaterial.js @@ -12,7 +12,6 @@ class PointsMaterial extends RawShaderMaterial { this.uniforms.size = new Uniform(size); this.uniforms.resolution = new Uniform(new Vector2(window.innerWidth, window.innerHeight)); this.uniforms.pickingMode = new Uniform(false); - this.uniforms.density = new Uniform(0.01); this.uniforms.opacity = new Uniform(1.0); this.uniforms.useCustomColor = new Uniform(false); this.uniforms.customColor = new Uniform(new Color()); diff --git a/src/Renderer/Shader/PointsVS.glsl b/src/Renderer/Shader/PointsVS.glsl index 26c6877248..d0ef6c19e0 100644 --- a/src/Renderer/Shader/PointsVS.glsl +++ b/src/Renderer/Shader/PointsVS.glsl @@ -10,7 +10,6 @@ uniform mat4 projectionMatrix; uniform mat4 modelViewMatrix; uniform vec2 resolution; uniform bool pickingMode; -uniform float density; // points per on screen pixels attribute vec4 unique_id; attribute vec3 color; diff --git a/test/pointcloudprocessing_unit_test.js b/test/pointcloudprocessing_unit_test.js index f394290217..a437ac65fc 100644 --- a/test/pointcloudprocessing_unit_test.js +++ b/test/pointcloudprocessing_unit_test.js @@ -2,6 +2,7 @@ import PointCloudProcessing from '../src/Process/PointCloudProcessing'; /* global describe, it */ const assert = require('assert'); +const context = { camera: { height: 1, camera3D: { fov: 1 } } }; describe('preUpdate', function () { it('should return root if no change source', () => { @@ -9,7 +10,7 @@ describe('preUpdate', function () { const sources = new Set(); assert.equal( layer.root, - PointCloudProcessing.preUpdate(null, layer, sources)[0]); + PointCloudProcessing.preUpdate(context, layer, sources)[0]); }); it('should return root if no common ancestors', () => { @@ -21,7 +22,7 @@ describe('preUpdate', function () { sources.add(elt2); assert.equal( layer.root, - PointCloudProcessing.preUpdate(null, layer, sources)[0]); + PointCloudProcessing.preUpdate(context, layer, sources)[0]); }); it('should return common ancestor', () => { @@ -36,7 +37,7 @@ describe('preUpdate', function () { layer.root.findChildrenByName = (name) => { assert.equal('12', name); }; - PointCloudProcessing.preUpdate(null, layer, sources); + PointCloudProcessing.preUpdate(context, layer, sources); }); it('should not search ancestors if layer are different root if no common ancestors', () => { @@ -49,6 +50,6 @@ describe('preUpdate', function () { layer.root.findChildrenByName = (name) => { assert.equal('12', name); }; - PointCloudProcessing.preUpdate(null, layer, sources); + PointCloudProcessing.preUpdate(context, layer, sources); }); }); diff --git a/utils/debug/PointCloudDebug.js b/utils/debug/PointCloudDebug.js index 83afd5e2d0..5732cccbc6 100644 --- a/utils/debug/PointCloudDebug.js +++ b/utils/debug/PointCloudDebug.js @@ -35,6 +35,8 @@ export default { layer.debugUI = datUi.addFolder(`${layer.id}`); + layer.debugUI.add(layer, 'sseThreshold').name('SSE threshold') + .onChange(() => view.notifyChange(true)); layer.debugUI.add(layer, 'octreeDepthLimit', -1, 20).name('Depth limit') .onChange(() => view.notifyChange(true)); layer.debugUI.add(layer, 'pointBudget', 1, 15000000).name('Max point count') @@ -46,8 +48,6 @@ export default { const surf = layer.debugUI.addFolder('Surface Method params'); surf.add(layer, 'pointSize', 0, 15).name('Point Size') .onChange(() => view.notifyChange(true)); - surf.add(layer, 'overdraw', 1, 5).name('Overdraw') - .onChange(() => view.notifyChange(true)); surf.add(layer, 'opacity', 0, 1).name('Opacity') .onChange(() => view.notifyChange(true));