@google/model-viewer
Version:
Easily display interactive 3D models on the web and in AR!
364 lines (346 loc) • 11.1 kB
JavaScript
/*
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import './images-4-up.js';
import '@polymer/paper-slider';
import { html, LitElement, property } from 'lit-element';
import { ImageAccessor } from '../image-accessor.js';
export class AnalysisView extends LitElement {
constructor() {
super();
this.leftImage = null;
this.rightImage = null;
this.analysisResult = null;
this.leftImageAccessor = null;
this.rightImageAccessor = null;
this.blackWhiteImageAccessor = null;
this.deltaImageAccessor = null;
this.threshold = 5;
const channel = new MessageChannel();
this.port = channel.port1;
this.worker = new Worker('../../dist/image-comparison-worker.js');
this.worker.postMessage('connect', [channel.port2]);
this.port.onmessage = (event) => this.onMessage(event);
this.port.start();
}
onMessage(event) {
const data = event.data;
const { type } = data;
switch (type) {
case 'analysis-completed':
this.analysisResult = data.result;
break;
default:
break;
}
}
async connectedCallback() {
super.connectedCallback && super.connectedCallback();
document.body.addEventListener('click', (event) => {
const originatingElement = event.path[0];
if (originatingElement.tagName !== 'IMG') {
return;
}
if (originatingElement.parentNode
.classList.contains('selected')) {
this.deselect(originatingElement);
}
else {
this.select(originatingElement);
}
});
await this.updateComplete;
const candidateCanvas = this.shadowRoot.querySelector('canvas[slot="top-left"]')
.transferControlToOffscreen();
const goldenCanvas = this.shadowRoot.querySelector('canvas[slot="top-right"]')
.transferControlToOffscreen();
const blackWhiteCanvas = this.shadowRoot.querySelector('canvas[slot="bottom-left"]')
.transferControlToOffscreen();
const deltaCanvas = this.shadowRoot.querySelector('canvas[slot="bottom-right"]')
.transferControlToOffscreen();
this.port.postMessage({
type: 'canvases-ready',
candidateCanvas,
goldenCanvas,
blackWhiteCanvas,
deltaCanvas
}, [candidateCanvas, goldenCanvas, blackWhiteCanvas, deltaCanvas]);
}
get canCompareImages() {
return this.leftImage != null && this.rightImage != null;
}
deselect(element) {
if (this.leftImage === element) {
this.leftImage = this.rightImage;
this.rightImage = null;
}
else if (this.rightImage === element) {
this.rightImage = null;
}
element.parentNode.classList.remove('selected');
}
select(element) {
if (this.leftImage == null) {
this.leftImage = element;
}
else if (this.rightImage == null) {
this.rightImage = element;
}
else {
return;
}
element.parentNode.classList.add('selected');
}
enterAnalysisView() {
if (this.canCompareImages) {
this.classList.add('compare');
}
}
exitAnalysisView() {
this.classList.remove('compare');
}
reset() {
if (this.rightImage != null) {
this.deselect(this.rightImage);
}
if (this.leftImage != null) {
this.deselect(this.leftImage);
}
}
get comparisonDimensions() {
if (this.canCompareImages) {
const width = Math.max(this.leftImage.naturalWidth, this.rightImage.naturalWidth);
const height = Math.max(this.leftImage.naturalHeight, this.rightImage.naturalHeight);
return { width, height };
}
return { width: 0, height: 0 };
}
async updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('analysisResult') &&
this.analysisResult != null && this.canCompareImages) {
const { imageBuffers } = this.analysisResult;
const { width, height } = this.comparisonDimensions;
this.blackWhiteImageAccessor = ImageAccessor.fromArrayBuffer(imageBuffers.blackWhite, width, height);
this.deltaImageAccessor =
ImageAccessor.fromArrayBuffer(imageBuffers.delta, width, height);
}
if ((changedProperties.has('leftImage') ||
changedProperties.has('rightImage')) &&
this.canCompareImages) {
this.leftImageAccessor = ImageAccessor.fromImageElement(this.leftImage);
this.rightImageAccessor =
ImageAccessor.fromImageElement(this.rightImage);
const { width, height } = this.comparisonDimensions;
const candidateImageBuffer = this.leftImageAccessor.toArrayBuffer();
const goldenImageBuffer = this.rightImageAccessor.toArrayBuffer();
this.port.postMessage({
type: 'images-assigned',
candidateImageBuffer,
goldenImageBuffer,
dimensions: { width, height }
}, [
candidateImageBuffer,
goldenImageBuffer
]);
this.port.postMessage({ type: 'threshold-changed', threshold: this.threshold });
}
if (changedProperties.has('threshold') && this.canCompareImages) {
this.port.postMessage({ type: 'threshold-changed', threshold: this.threshold });
}
}
render() {
let instructions;
if (this.canCompareImages) {
instructions = html `
<button ="${() => this.enterAnalysisView()}">
Compare selected images
</button>
<button ="${() => this.reset()}" class="no-border">
Clear selections
</button>`;
}
else {
instructions = html `<p>Select two images to compare them!</p>`;
}
return html `
<style>
:host {
display: block;
position: fixed;
width: 100vw;
bottom: 0;
left: 0;
font-family: Google Sans, sans-serif;
background-color: #37474f;
color: #fff;
box-shadow: 0px 3px 12px rgba(100, 100, 100, 0.3);
}
#instructions {
text-align: center;
font-style: italic;
padding: 1em;
}
button {
font-family: Google Sans, sans-serif;
text-transform: uppercase;
color: #fff;
background-color: #37474f;
border: 1px solid #fff;
border-radius: 3px;
font-size: 1.1em;
font-weight: 200;
padding: 0.45em 0.85em;
cursor: pointer;
}
button {
margin: 0.35em 0;
}
button:hover, button:focus {
background-color: #62727b;
}
#comparison {
display: flex;
position: fixed;
flex-direction: column;
align-items: stretch;
justify-content: center;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
background-color: #37474f;
transform: translateY(1vh);
opacity: 0;
pointer-events: none;
transition: transform 0.3s, opacity 0.3s;
}
:host(.compare) #comparison {
transform: translateY(0);
opacity: 1;
pointer-events: unset;
}
button.no-border {
border: none;
}
.exit {
margin: 0;
border: 0;
font-size: 0;
line-height: 0;
position: absolute;
right: 32px;
top: 32px;
width: 32px;
height: 32px;
opacity: 0.75;
}
.exit:hover {
opacity: 1;
}
.exit:before, .exit:after {
content: ' ';
position: absolute;
top: 0px;
left: 15px;
height: 33px;
width: 2px;
background-color: #fff;
}
.exit:before {
transform: rotate(45deg);
}
.exit:after {
transform: rotate(-45deg);
}
images-4-up {
flex: 1 1 100%;
}
#controls {
display: flex;
align-items: center;
justify-content: center;
flex: 1 1 90px;
color: #fff;
--primary-text-color: #fff;
--paper-slider-active-color: #fff;
--paper-slider-pin-color: #fff;
--paper-slider-knob-color: #fff;
--paper-slider-font-color: #444;
}
canvas {
width: 100%;
height: 100%;
}
</style>
<div id="instructions">
${instructions}
</div>
<div id="comparison">
<images-4-up .dimensions="${this.comparisonDimensions}"
.topLeftImageAccessor="${this.leftImageAccessor}"
.topRightImageAccessor="${this.rightImageAccessor}"
.bottomLeftImageAccessor="${this.blackWhiteImageAccessor}"
.bottomRightImageAccessor="${this.deltaImageAccessor}">
<canvas slot="top-left"></canvas>
<canvas slot="top-right"></canvas>
<canvas slot="bottom-left"></canvas>
<canvas slot="bottom-right"></canvas>
</images-4-up>
<div id="controls">
<label id="threshold-label">Threshold</label>
<paper-slider
="${(event) => this.threshold =
parseInt(event.target.value, 10)}"
aria-labelledby="threshold-label"
min="0" max="10" step="1" pin snaps value="5" editable>
</paper-slider>
</div>
<button class="exit" ="${() => this.exitAnalysisView()}">Exit</button>
</div>`;
}
}
__decorate([
property({ type: Object })
], AnalysisView.prototype, "leftImage", void 0);
__decorate([
property({ type: Object })
], AnalysisView.prototype, "rightImage", void 0);
__decorate([
property({ type: Object })
], AnalysisView.prototype, "analysisResult", void 0);
__decorate([
property({ type: Object })
], AnalysisView.prototype, "leftImageAccessor", void 0);
__decorate([
property({ type: Object })
], AnalysisView.prototype, "rightImageAccessor", void 0);
__decorate([
property({ type: Object })
], AnalysisView.prototype, "blackWhiteImageAccessor", void 0);
__decorate([
property({ type: Object })
], AnalysisView.prototype, "deltaImageAccessor", void 0);
__decorate([
property({ type: Number })
], AnalysisView.prototype, "threshold", void 0);
customElements.define('analysis-view', AnalysisView);
//# sourceMappingURL=analysis-view.js.map