Compare commits
50 Commits
nb-conflic
...
compass-ro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98a048062f | ||
|
|
9b114c49df | ||
|
|
2c0c998e29 | ||
|
|
a001e07600 | ||
|
|
4b40233bf3 | ||
|
|
f3f833a337 | ||
|
|
e6e8b8e048 | ||
|
|
6a2c079336 | ||
|
|
334aeb42ae | ||
|
|
5900bb0d98 | ||
|
|
a2c350b105 | ||
|
|
174f212328 | ||
|
|
a28ec45f71 | ||
|
|
fcc6bb9873 | ||
|
|
45578b113f | ||
|
|
5ef14b0975 | ||
|
|
2be429a04f | ||
|
|
3804fe1a1e | ||
|
|
7576673e77 | ||
|
|
63e04caab6 | ||
|
|
956cfbd01f | ||
|
|
6c77be32c7 | ||
|
|
f732167e02 | ||
|
|
1d56fd98dc | ||
|
|
b43fef6e21 | ||
|
|
d04c29345b | ||
|
|
24b96cdb47 | ||
|
|
14ce4a1aa0 | ||
|
|
28d97be60e | ||
|
|
1f6e91c6b5 | ||
|
|
0b078497f1 | ||
|
|
060a1b17db | ||
|
|
417f81b7fd | ||
|
|
f219394abd | ||
|
|
4e5c74ecef | ||
|
|
218530e436 | ||
|
|
0890499a2b | ||
|
|
d9dad09dfd | ||
|
|
9af5df0f20 | ||
|
|
1580a61092 | ||
|
|
c236444a05 | ||
|
|
39c1eb1d5b | ||
|
|
95caab944d | ||
|
|
ac89e51d1b | ||
|
|
85d9ed8287 | ||
|
|
aedc24a2da | ||
|
|
823eda4465 | ||
|
|
7eaa1d3e2b | ||
|
|
4633436cbd | ||
|
|
b68a7e27c9 |
@@ -1,3 +1,25 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is 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.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import ImageryViewLayout from './components/ImageryViewLayout.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
|
||||
130
src/plugins/imagery/components/Compass/Compass.vue
Normal file
130
src/plugins/imagery/components/Compass/Compass.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is 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.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="c-compass"
|
||||
:style="compassDimensionsStyle"
|
||||
>
|
||||
<CompassHUD
|
||||
v-if="shouldDisplayCompassHUD"
|
||||
:rover-heading="roverHeading"
|
||||
:rover-roll="roverRoll"
|
||||
:sun-heading="sunHeading"
|
||||
:camera-field-of-view="cameraFieldOfView"
|
||||
:camera-pan="cameraPan"
|
||||
/>
|
||||
<CompassRose
|
||||
v-if="shouldDisplayCompassRose"
|
||||
:rover-heading="roverHeading"
|
||||
:sun-heading="sunHeading"
|
||||
:camera-field-of-view="cameraFieldOfView"
|
||||
:camera-pan="cameraPan"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CompassHUD from './CompassHUD.vue';
|
||||
import CompassRose from './CompassRose.vue';
|
||||
|
||||
const CAM_FIELD_OF_VIEW = 70;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CompassHUD,
|
||||
CompassRose
|
||||
},
|
||||
props: {
|
||||
containerWidth: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
containerHeight: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
naturalAspectRatio: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
image: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
shouldDisplayCompassRose() {
|
||||
return this.roverHeading !== undefined;
|
||||
},
|
||||
shouldDisplayCompassHUD() {
|
||||
return this.roverHeading !== undefined;
|
||||
},
|
||||
// degrees from north heading
|
||||
roverHeading() {
|
||||
return this.image['Rover Heading'];
|
||||
},
|
||||
roverRoll() {
|
||||
return this.image['Rover Roll'];
|
||||
},
|
||||
roverYaw() {
|
||||
return this.image['Rover Yaw'];
|
||||
},
|
||||
roverPitch() {
|
||||
return this.image['Rover Pitch'];
|
||||
},
|
||||
// degrees from north heading
|
||||
sunHeading() {
|
||||
return this.image['Sun Orientation'];
|
||||
},
|
||||
// degrees from rover heading
|
||||
cameraPan() {
|
||||
return this.image['Camera Pan'];
|
||||
},
|
||||
cameraTilt() {
|
||||
return this.image['Camera Tilt'];
|
||||
},
|
||||
cameraFieldOfView() {
|
||||
return CAM_FIELD_OF_VIEW;
|
||||
},
|
||||
compassDimensionsStyle() {
|
||||
const containerAspectRatio = this.containerWidth / this.containerHeight;
|
||||
|
||||
let width;
|
||||
let height;
|
||||
|
||||
if (containerAspectRatio < this.naturalAspectRatio) {
|
||||
width = '100%';
|
||||
height = `${ this.containerWidth / this.naturalAspectRatio }px`;
|
||||
} else {
|
||||
width = `${ this.containerHeight * this.naturalAspectRatio }px`;
|
||||
height = '100%';
|
||||
}
|
||||
|
||||
return {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
171
src/plugins/imagery/components/Compass/CompassHUD.vue
Normal file
171
src/plugins/imagery/components/Compass/CompassHUD.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is 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.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="c-compass__hud c-hud"
|
||||
:style="skewCompassHUDStyle"
|
||||
>
|
||||
<div
|
||||
v-for="point in visibleCompassPoints"
|
||||
:key="point.direction"
|
||||
:class="point.class"
|
||||
:style="point.style"
|
||||
>
|
||||
{{ point.direction }}
|
||||
</div>
|
||||
<div
|
||||
v-if="isSunInRange"
|
||||
ref="sun"
|
||||
class="c-hud__sun"
|
||||
:style="sunPositionStyle"
|
||||
></div>
|
||||
<div class="c-hud__range"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
normalizeDegrees,
|
||||
inRange,
|
||||
percentOfRange
|
||||
} from './utils';
|
||||
|
||||
const COMPASS_POINTS = [
|
||||
{
|
||||
direction: 'N',
|
||||
class: 'c-hud__dir',
|
||||
degrees: 0
|
||||
},
|
||||
{
|
||||
direction: 'NE',
|
||||
class: 'c-hud__dir--sub',
|
||||
degrees: 45
|
||||
},
|
||||
{
|
||||
direction: 'E',
|
||||
class: 'c-hud__dir',
|
||||
degrees: 90
|
||||
},
|
||||
{
|
||||
direction: 'SE',
|
||||
class: 'c-hud__dir--sub',
|
||||
degrees: 135
|
||||
},
|
||||
{
|
||||
direction: 'S',
|
||||
class: 'c-hud__dir',
|
||||
degrees: 180
|
||||
},
|
||||
{
|
||||
direction: 'SW',
|
||||
class: 'c-hud__dir--sub',
|
||||
degrees: 225
|
||||
},
|
||||
{
|
||||
direction: 'W',
|
||||
class: 'c-hud__dir',
|
||||
degrees: 270
|
||||
},
|
||||
{
|
||||
direction: 'NW',
|
||||
class: 'c-hud__dir--sub',
|
||||
degrees: 315
|
||||
}
|
||||
];
|
||||
|
||||
export default {
|
||||
props: {
|
||||
roverHeading: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
roverRoll: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
sunHeading: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
cameraFieldOfView: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
cameraPan: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
skewCompassHUDStyle() {
|
||||
if (this.roverRoll === undefined || this.roverRoll === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const origin = this.roverRoll > 0 ? 'left bottom' : 'right top';
|
||||
|
||||
return {
|
||||
'transform-origin': origin,
|
||||
transform: `skew(0, ${ this.roverRoll }deg`
|
||||
};
|
||||
},
|
||||
visibleCompassPoints() {
|
||||
return COMPASS_POINTS
|
||||
.filter(point => inRange(point.degrees, this.visibleRange))
|
||||
.map(point => {
|
||||
const percentage = percentOfRange(point.degrees, this.visibleRange);
|
||||
point.style = Object.assign(
|
||||
{ left: `${ percentage * 100 }%` }
|
||||
);
|
||||
|
||||
return point;
|
||||
});
|
||||
},
|
||||
isSunInRange() {
|
||||
return inRange(this.normalizedSunHeading, this.visibleRange);
|
||||
},
|
||||
sunPositionStyle() {
|
||||
const percentage = percentOfRange(this.normalizedSunHeading, this.visibleRange);
|
||||
|
||||
return {
|
||||
left: `${ percentage * 100 }%`
|
||||
};
|
||||
},
|
||||
normalizedSunHeading() {
|
||||
return normalizeDegrees(this.sunHeading);
|
||||
},
|
||||
normalizedRoverHeading() {
|
||||
return normalizeDegrees(this.roverHeading);
|
||||
},
|
||||
visibleRange() {
|
||||
const min = normalizeDegrees(this.normalizedRoverHeading + this.cameraPan - this.cameraFieldOfView / 2);
|
||||
const max = normalizeDegrees(this.normalizedRoverHeading + this.cameraPan + this.cameraFieldOfView / 2);
|
||||
|
||||
return [
|
||||
min,
|
||||
max
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
262
src/plugins/imagery/components/Compass/CompassRose.vue
Normal file
262
src/plugins/imagery/components/Compass/CompassRose.vue
Normal file
@@ -0,0 +1,262 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is 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.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="c-direction-rose"
|
||||
@click="toggleBezelLock"
|
||||
>
|
||||
<div
|
||||
class="c-nsew"
|
||||
:style="rotateFrameStyle"
|
||||
>
|
||||
<svg
|
||||
class="c-nsew__minor-ticks"
|
||||
viewBox="0 0 100 100"
|
||||
>
|
||||
<rect
|
||||
class="c-nsew__tick c-tick-ne"
|
||||
x="49"
|
||||
y="0"
|
||||
width="2"
|
||||
height="5"
|
||||
/>
|
||||
<rect
|
||||
class="c-nsew__tick c-tick-se"
|
||||
x="95"
|
||||
y="49"
|
||||
width="5"
|
||||
height="2"
|
||||
/>
|
||||
<rect
|
||||
class="c-nsew__tick c-tick-sw"
|
||||
x="49"
|
||||
y="95"
|
||||
width="2"
|
||||
height="5"
|
||||
/>
|
||||
<rect
|
||||
class="c-nsew__tick c-tick-nw"
|
||||
x="0"
|
||||
y="49"
|
||||
width="5"
|
||||
height="2"
|
||||
/>
|
||||
|
||||
</svg>
|
||||
|
||||
<svg
|
||||
class="c-nsew__ticks"
|
||||
viewBox="0 0 100 100"
|
||||
>
|
||||
<polygon
|
||||
class="c-nsew__tick c-tick-n"
|
||||
points="50,0 57,5 43,5"
|
||||
/>
|
||||
<rect
|
||||
class="c-nsew__tick c-tick-e"
|
||||
x="95"
|
||||
y="49"
|
||||
width="5"
|
||||
height="2"
|
||||
/>
|
||||
<rect
|
||||
class="c-nsew__tick c-tick-w"
|
||||
x="0"
|
||||
y="49"
|
||||
width="5"
|
||||
height="2"
|
||||
/>
|
||||
<rect
|
||||
class="c-nsew__tick c-tick-s"
|
||||
x="49"
|
||||
y="95"
|
||||
width="2"
|
||||
height="5"
|
||||
/>
|
||||
|
||||
<text
|
||||
class="c-nsew__label c-label-n"
|
||||
text-anchor="middle"
|
||||
:transform="northTextTransform"
|
||||
>N</text>
|
||||
<text
|
||||
class="c-nsew__label c-label-e"
|
||||
text-anchor="middle"
|
||||
:transform="eastTextTransform"
|
||||
>E</text>
|
||||
<text
|
||||
class="c-nsew__label c-label-w"
|
||||
text-anchor="middle"
|
||||
:transform="southTextTransform"
|
||||
>W</text>
|
||||
<text
|
||||
class="c-nsew__label c-label-s"
|
||||
text-anchor="middle"
|
||||
:transform="westTextTransform"
|
||||
>S</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="c-rover-body"
|
||||
:style="roverHeadingStyle"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="c-sun"
|
||||
:style="sunHeadingStyle"
|
||||
></div>
|
||||
|
||||
<div
|
||||
v-if="showCameraFOV"
|
||||
class="c-cam-field"
|
||||
:style="cameraFOVHeadingStyle"
|
||||
>
|
||||
<div class="cam-field-half cam-field-half-l">
|
||||
<div
|
||||
class="cam-field-area"
|
||||
:style="cameraFOVStyleLeftHalf"
|
||||
></div>
|
||||
</div>
|
||||
<div class="cam-field-half cam-field-half-r">
|
||||
<div
|
||||
class="cam-field-area"
|
||||
:style="cameraFOVStyleRightHalf"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { normalizeDegrees } from './utils';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
roverHeading: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
sunHeading: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
cameraFieldOfView: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
cameraPan: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
lockBezel: true
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
compassRoverHeading() {
|
||||
return this.lockBezel ? normalizeDegrees(this.roverHeading) : 0;
|
||||
},
|
||||
north() {
|
||||
return normalizeDegrees(this.compassRoverHeading - this.roverHeading);
|
||||
},
|
||||
rotateFrameStyle() {
|
||||
return { transform: `rotate(${ this.north }deg)` };
|
||||
},
|
||||
northTextTransform() {
|
||||
return this.cardinalPointsTextTransform.north;
|
||||
},
|
||||
eastTextTransform() {
|
||||
return this.cardinalPointsTextTransform.east;
|
||||
},
|
||||
southTextTransform() {
|
||||
return this.cardinalPointsTextTransform.south;
|
||||
},
|
||||
westTextTransform() {
|
||||
return this.cardinalPointsTextTransform.west;
|
||||
},
|
||||
cardinalPointsTextTransform() {
|
||||
/**
|
||||
* cardinal points text must be rotated
|
||||
* in the opposite direction that north is rotated
|
||||
* to keep text vertically oriented
|
||||
*/
|
||||
const rotation = `rotate(${ -this.north })`;
|
||||
|
||||
return {
|
||||
north: `translate(50,15) ${ rotation }`,
|
||||
east: `translate(87,50) ${ rotation }`,
|
||||
south: `translate(13,50) ${ rotation }`,
|
||||
west: `translate(50,87) ${ rotation }`
|
||||
};
|
||||
},
|
||||
roverHeadingStyle() {
|
||||
return {
|
||||
transform: `translateX(-50%) rotate(${ this.compassRoverHeading }deg)`
|
||||
};
|
||||
},
|
||||
cameraFOVHeading() {
|
||||
return this.compassRoverHeading + this.cameraPan;
|
||||
},
|
||||
cameraFOVHeadingStyle() {
|
||||
return {
|
||||
transform: `rotate(${ this.cameraFOVHeading }deg)`
|
||||
};
|
||||
},
|
||||
sunHeadingStyle() {
|
||||
const rotation = normalizeDegrees(this.north + this.sunHeading);
|
||||
|
||||
return {
|
||||
transform: `rotate(${ rotation }deg)`
|
||||
};
|
||||
},
|
||||
showCameraFOV() {
|
||||
return this.cameraPan !== undefined && this.cameraFieldOfView > 0;
|
||||
},
|
||||
// left half of camera field of view
|
||||
// rotated counter-clockwise from camera field of view heading
|
||||
cameraFOVStyleLeftHalf() {
|
||||
return {
|
||||
transform: `translateX(50%) rotate(${ -this.cameraFieldOfView / 2 }deg)`
|
||||
};
|
||||
},
|
||||
// right half of camera field of view
|
||||
// rotated clockwise from camera field of view heading
|
||||
cameraFOVStyleRightHalf() {
|
||||
return {
|
||||
transform: `translateX(-50%) rotate(${ this.cameraFieldOfView / 2 }deg)`
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleBezelLock() {
|
||||
this.lockBezel = !this.lockBezel;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
216
src/plugins/imagery/components/Compass/compass.scss
Normal file
216
src/plugins/imagery/components/Compass/compass.scss
Normal file
@@ -0,0 +1,216 @@
|
||||
/***************************** THEME/UI CONSTANTS AND MIXINS */
|
||||
$interfaceKeyColor: #00B9C5;
|
||||
$elemBg: rgba(black, 0.7);
|
||||
|
||||
@mixin sun($position: 'circle closest-side') {
|
||||
$color: #ff9900;
|
||||
$gradEdgePerc: 60%;
|
||||
background: radial-gradient(#{$position}, $color, $color $gradEdgePerc, rgba($color, 0.4) $gradEdgePerc + 5%, transparent);
|
||||
|
||||
}
|
||||
|
||||
.c-compass {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
@include userSelectNone;
|
||||
}
|
||||
|
||||
/***************************** COMPASS HUD */
|
||||
.c-hud {
|
||||
// To be placed within a imagery view, in the bounding box of the image
|
||||
$m: 1px;
|
||||
$padTB: 2px;
|
||||
$padLR: $padTB;
|
||||
background: $elemBg;
|
||||
border-radius: 3px;
|
||||
color: $interfaceKeyColor;
|
||||
font-size: 0.8em;
|
||||
position: absolute;
|
||||
top: $m; right: $m; left: $m;
|
||||
height: 18px;
|
||||
|
||||
svg, div {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&__display {
|
||||
height: 30px;
|
||||
pointer-events: all;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&__range {
|
||||
border: 1px solid $interfaceKeyColor;
|
||||
border-top-color: transparent;
|
||||
position: absolute;
|
||||
top: 50%; right: $padLR; bottom: $padTB; left: $padLR;
|
||||
}
|
||||
|
||||
[class*="__dir"] {
|
||||
// NSEW
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
text-shadow: black 0 0 3px;
|
||||
top: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
[class*="__dir--sub"] {
|
||||
font-weight: normal;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&__sun {
|
||||
$s: 10px;
|
||||
@include sun('circle farthest-side at bottom');
|
||||
bottom: $padTB + 2px;
|
||||
height: $s; width: $s*2;
|
||||
opacity: 0.8;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***************************** COMPASS DIRECTIONS */
|
||||
.c-nsew {
|
||||
$color: $interfaceKeyColor;
|
||||
$inset: 7%;
|
||||
$tickHeightPerc: 15%;
|
||||
text-shadow: black 0 0 10px;
|
||||
top: $inset; right: $inset; bottom: $inset; left: $inset;
|
||||
z-index: 3;
|
||||
|
||||
&__tick,
|
||||
&__label {
|
||||
fill: $color;
|
||||
}
|
||||
|
||||
&__minor-ticks {
|
||||
opacity: 0.5;
|
||||
transform-origin: center;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
&__label {
|
||||
dominant-baseline: central;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.c-label-n {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************** CAMERA FIELD ANGLE */
|
||||
.c-cam-field {
|
||||
$color: white;
|
||||
opacity: 0.2;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
|
||||
.cam-field-half {
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
.cam-field-area {
|
||||
background: $color;
|
||||
top: -30%;
|
||||
right: 0;
|
||||
bottom: -30%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
// clip-paths overlap a bit to avoid a gap between halves
|
||||
&-l {
|
||||
clip-path: polygon(0 0, 50.5% 0, 50.5% 100%, 0 100%);
|
||||
.cam-field-area {
|
||||
transform-origin: left center;
|
||||
}
|
||||
}
|
||||
|
||||
&-r {
|
||||
clip-path: polygon(49.5% 0, 100% 0, 100% 100%, 49.5% 100%);
|
||||
.cam-field-area {
|
||||
transform-origin: right center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************** ROVER BODY */
|
||||
.c-rover-body {
|
||||
$color: $interfaceKeyColor;
|
||||
$s: 30%;
|
||||
background: $color;
|
||||
border-radius: 3px;
|
||||
height: $s; width: $s;
|
||||
left: 50%; top: 50%;
|
||||
opacity: 0.4;
|
||||
transform-origin: center top;
|
||||
|
||||
&:before {
|
||||
// Direction arrow
|
||||
$color: rgba(black, 0.5);
|
||||
$arwPointerY: 60%;
|
||||
$arwBodyOffset: 25%;
|
||||
background: $color;
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 10%; right: 20%; bottom: 50%; left: 20%;
|
||||
clip-path: polygon(50% 0, 100% $arwPointerY, 100%-$arwBodyOffset $arwPointerY, 100%-$arwBodyOffset 100%, $arwBodyOffset 100%, $arwBodyOffset $arwPointerY, 0 $arwPointerY);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************** DIRECTION ROSE */
|
||||
.c-direction-rose {
|
||||
$d: 100px;
|
||||
$c2: rgba(white, 0.1);
|
||||
background: $elemBg;
|
||||
background-image: radial-gradient(circle closest-side, transparent, transparent 80%, $c2);
|
||||
width: $d;
|
||||
height: $d;
|
||||
transform-origin: 0 0;
|
||||
position: absolute;
|
||||
bottom: 10px; left: 10px;
|
||||
clip-path: circle(50% at 50% 50%);
|
||||
border-radius: 100%;
|
||||
|
||||
svg, div {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
// Sun
|
||||
.c-sun {
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
&:before {
|
||||
$s: 35%;
|
||||
@include sun();
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
opacity: 0.7;
|
||||
top: 0; left: 50%;
|
||||
height:$s; width: $s;
|
||||
transform: translate(-50%, -60%);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/plugins/imagery/components/Compass/utils.js
Normal file
44
src/plugins/imagery/components/Compass/utils.js
Normal file
@@ -0,0 +1,44 @@
|
||||
export function normalizeDegrees(degrees) {
|
||||
const base = degrees % 360;
|
||||
|
||||
return base >= 0 ? base : 360 + base;
|
||||
}
|
||||
|
||||
export function inRange(degrees, [min, max]) {
|
||||
return min > max
|
||||
? (degrees >= min && degrees < 360) || (degrees <= max && degrees >= 0)
|
||||
: degrees >= min && degrees <= max;
|
||||
}
|
||||
|
||||
export function percentOfRange(degrees, [min, max]) {
|
||||
let distance = degrees;
|
||||
let minRange = min;
|
||||
let maxRange = max;
|
||||
|
||||
if (min > max) {
|
||||
if (distance < max) {
|
||||
distance += 360;
|
||||
}
|
||||
|
||||
maxRange += 360;
|
||||
}
|
||||
|
||||
return (distance - minRange) / (maxRange - minRange);
|
||||
}
|
||||
|
||||
export function normalizeSemiCircleDegrees(rawDegrees) {
|
||||
// in case tony hawk is providing us degrees
|
||||
let degrees = rawDegrees % 360;
|
||||
|
||||
// westward degrees are between 0 and -180 exclusively
|
||||
if (degrees > 180) {
|
||||
degrees = degrees - 360;
|
||||
}
|
||||
|
||||
// eastward degrees are between 0 and 180 inclusively
|
||||
if (degrees <= -180) {
|
||||
degrees = 360 - degrees;
|
||||
}
|
||||
|
||||
return degrees;
|
||||
}
|
||||
@@ -1,3 +1,25 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is 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.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div
|
||||
tabindex="0"
|
||||
@@ -36,14 +58,23 @@
|
||||
<div class="c-imagery__main-image__bg"
|
||||
:class="{'paused unnsynced': isPaused,'stale':false }"
|
||||
>
|
||||
<div class="c-imagery__main-image__image js-imageryView-image"
|
||||
:style="{
|
||||
'background-image': imageUrl ? `url(${imageUrl})` : 'none',
|
||||
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`
|
||||
}"
|
||||
:data-openmct-image-timestamp="time"
|
||||
:data-openmct-object-keystring="keyString"
|
||||
></div>
|
||||
<img
|
||||
ref="focusedImage"
|
||||
class="c-imagery__main-image__image js-imageryView-image"
|
||||
:src="imageUrl"
|
||||
:style="{
|
||||
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`
|
||||
}"
|
||||
:data-openmct-image-timestamp="time"
|
||||
:data-openmct-object-keystring="keyString"
|
||||
>
|
||||
<Compass
|
||||
v-if="shouldDisplayCompass"
|
||||
:container-width="imageContainerWidth"
|
||||
:container-height="imageContainerHeight"
|
||||
:natural-aspect-ratio="focusedImageNaturalAspectRatio"
|
||||
:image="focusedImage"
|
||||
/>
|
||||
</div>
|
||||
<div class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-buttons">
|
||||
<button class="c-nav c-nav--prev"
|
||||
@@ -97,7 +128,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import Compass from './Compass/Compass.vue';
|
||||
|
||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||
const REFRESH_CSS_MS = 500;
|
||||
@@ -117,6 +150,9 @@ const ARROW_LEFT = 37;
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
components: {
|
||||
Compass
|
||||
},
|
||||
data() {
|
||||
let timeSystem = this.openmct.time.timeSystem();
|
||||
|
||||
@@ -137,7 +173,13 @@ export default {
|
||||
refreshCSS: false,
|
||||
keyString: undefined,
|
||||
focusedImageIndex: undefined,
|
||||
numericDuration: undefined
|
||||
focusedImageRelatedData: {},
|
||||
numericDuration: undefined,
|
||||
metadataEndpoints: {},
|
||||
relatedTelemetry: {},
|
||||
focusedImageNaturalAspectRatio: undefined,
|
||||
imageContainerWidth: undefined,
|
||||
imageContainerHeight: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -195,15 +237,23 @@ export default {
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
shouldDisplayCompass() {
|
||||
return this.focusedImage !== undefined
|
||||
&& this.focusedImageNaturalAspectRatio !== undefined
|
||||
&& this.imageContainerWidth !== undefined
|
||||
&& this.imageContainerHeight !== undefined;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
focusedImageIndex() {
|
||||
this.trackDuration();
|
||||
this.resetAgeCSS();
|
||||
this.updateRelatedTelemetryForFocusedImage();
|
||||
this.getImageNaturalDimensions();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
async mounted() {
|
||||
// listen
|
||||
this.openmct.time.on('bounds', this.boundsChange);
|
||||
this.openmct.time.on('timeSystem', this.timeSystemChange);
|
||||
@@ -212,8 +262,17 @@ export default {
|
||||
// set
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.imageHints = this.metadata.valuesForHints(['image'])[0];
|
||||
this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
|
||||
this.imageFormatter = this.openmct.telemetry.getValueFormatter(this.metadata.valuesForHints(['image'])[0]);
|
||||
this.imageFormatter = this.openmct.telemetry.getValueFormatter(this.imageHints);
|
||||
this.roverKeys = ['Rover Heading', 'Rover Roll', 'Rover Yaw', 'Rover Pitch'];
|
||||
this.cameraKeys = ['Camera Pan', 'Camera Tilt'];
|
||||
this.sunKeys = ['Sun Orientation'];
|
||||
|
||||
// DELETE WHEN DONE
|
||||
if (!this.imageHints.relatedTelemetry) {
|
||||
this.temporaryForImageEnhancements();
|
||||
}
|
||||
|
||||
// initialize
|
||||
this.timeKey = this.timeSystem.key;
|
||||
@@ -222,6 +281,12 @@ export default {
|
||||
// kickoff
|
||||
this.subscribe();
|
||||
this.requestHistory();
|
||||
await this.initializeRelatedTelemetry();
|
||||
|
||||
// for when people are scrolling through images quickly
|
||||
_.debounce(this.updateRelatedTelemetryForFocusedImage, 400);
|
||||
|
||||
this.pollResizeImageContainerID = setInterval(this.pollResizeImageContainer, 300);
|
||||
},
|
||||
updated() {
|
||||
this.scrollToRight();
|
||||
@@ -236,8 +301,222 @@ export default {
|
||||
this.openmct.time.off('bounds', this.boundsChange);
|
||||
this.openmct.time.off('timeSystem', this.timeSystemChange);
|
||||
this.openmct.time.off('clock', this.clockChange);
|
||||
|
||||
// unsubscribe from related telemetry
|
||||
if (this.hasRelatedTelemetry) {
|
||||
for (let key of this.relatedTelemetry.keys) {
|
||||
if (this.relatedTelemetry[key].unsubscribe) {
|
||||
this.relatedTelemetry[key].unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearInterval(this.pollResizeImageContainerID);
|
||||
},
|
||||
methods: {
|
||||
// for local dev, to be DELETED
|
||||
temporaryForImageEnhancements() {
|
||||
|
||||
this.searchService = this.openmct.$injector.get('searchService');
|
||||
this.temporaryDev = true;
|
||||
|
||||
// mock related telemetry metadata
|
||||
this.imageHints.relatedTelemetry = {};
|
||||
|
||||
// populate temp keys in imageHints for local testing
|
||||
[...this.roverKeys, ...this.cameraKeys, ...this.sunKeys].forEach(key => {
|
||||
|
||||
this.imageHints.relatedTelemetry[key] = {
|
||||
dev: true,
|
||||
areEqual: function (valueOne, valueTwo) {
|
||||
const DECIMAL_COMPARISON_TOLERANCE = 1;
|
||||
const WHOLE = Math.pow(10, DECIMAL_COMPARISON_TOLERANCE);
|
||||
|
||||
return Math.floor(valueOne.toFixed(DECIMAL_COMPARISON_TOLERANCE) * WHOLE) === Math.floor(valueTwo.toFixed(DECIMAL_COMPARISON_TOLERANCE) * WHOLE);
|
||||
},
|
||||
realtime: {
|
||||
telemetryObjectId: key,
|
||||
valueKey: 'sin'
|
||||
},
|
||||
historical: {
|
||||
telemetryObjectId: key,
|
||||
valueKey: 'sin'
|
||||
},
|
||||
devInit: async () => {
|
||||
const searchResults = await this.searchService.query(key);
|
||||
const endpoint = searchResults.hits[0].id;
|
||||
const domainObject = await this.openmct.objects.get(endpoint);
|
||||
|
||||
return domainObject;
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
async initializeRelatedTelemetry() {
|
||||
if (this.imageHints.relatedTelemetry === undefined) {
|
||||
this.hasRelatedTelemetry = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// DELETE
|
||||
if (this.temporaryDev) {
|
||||
let searchIndexBuildDelay = new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, 3000);
|
||||
});
|
||||
|
||||
await searchIndexBuildDelay;
|
||||
}
|
||||
|
||||
let keys = Object.keys(this.imageHints.relatedTelemetry);
|
||||
|
||||
this.hasRelatedTelemetry = true;
|
||||
this.relatedTelemetry = {
|
||||
keys,
|
||||
...this.imageHints.relatedTelemetry
|
||||
};
|
||||
|
||||
// grab historical and subscribe to realtime
|
||||
for (let key of keys) {
|
||||
let historicalId;
|
||||
let realtimeId;
|
||||
|
||||
if (this.relatedTelemetry[key].historical) {
|
||||
if (this.relatedTelemetry[key].historical.telemetryObjectId) {
|
||||
historicalId = this.relatedTelemetry[key].historical.telemetryObjectId;
|
||||
} else {
|
||||
this.relatedTelemetry[key].historicalValuesOnTelemetry = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.relatedTelemetry[key].realtime && this.relatedTelemetry[key].realtime.telemetryObjectId) {
|
||||
realtimeId = this.relatedTelemetry[key].realtime.telemetryObjectId;
|
||||
}
|
||||
|
||||
// if we have a historical object id, then values will NOT be on the imagery datum
|
||||
if (historicalId) {
|
||||
|
||||
// DELETE temp
|
||||
if (this.relatedTelemetry[key].dev) {
|
||||
this.relatedTelemetry[key].historicalDomainObject = await this.relatedTelemetry[key].devInit();
|
||||
} else {
|
||||
this.relatedTelemetry[key].historicalDomainObject = await this.openmct.objects.get(historicalId);
|
||||
}
|
||||
|
||||
this.relatedTelemetry[key].requestLatestFor = async (datum) => {
|
||||
const options = {
|
||||
start: this.openmct.time.bounds().start,
|
||||
end: datum[this.timeKey],
|
||||
strategy: 'latest'
|
||||
};
|
||||
let results = await this.openmct.telemetry
|
||||
.request(this.relatedTelemetry[key].historicalDomainObject, options);
|
||||
|
||||
return results[results.length - 1];
|
||||
};
|
||||
}
|
||||
|
||||
if (realtimeId) {
|
||||
|
||||
if (this.relatedTelemetry[key].dev) {
|
||||
this.relatedTelemetry[key].realtimeDomainObject = await this.relatedTelemetry[key].devInit();
|
||||
} else {
|
||||
this.relatedTelemetry[key].realtimeDomainObject = await this.openmct.objects.get(realtimeId);
|
||||
}
|
||||
|
||||
// set up listeners
|
||||
this.relatedTelemetry[key].listeners = [];
|
||||
this.relatedTelemetry[key].subscribe = (callback) => {
|
||||
|
||||
if (!this.relatedTelemetry[key].isSubscribed) {
|
||||
this.subscribeToDataForKey(key);
|
||||
}
|
||||
|
||||
if (!this.relatedTelemetry[key].listeners.includes(callback)) {
|
||||
this.relatedTelemetry[key].listeners.push(callback);
|
||||
|
||||
return () => {
|
||||
this.relatedTelemetry[key].listeners.remove(callback);
|
||||
};
|
||||
} else {
|
||||
return () => {};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
async getMostRecentRelatedTelemetry(key, targetDatum) {
|
||||
if (!this.hasRelatedTelemetry) {
|
||||
throw new Error(`${this.domainObject.name} does not have any related telemetry`);
|
||||
}
|
||||
|
||||
if (!this.relatedTelemetry[key]) {
|
||||
throw new Error(`${key} does not exist on related telemetry`);
|
||||
}
|
||||
|
||||
let mostRecent;
|
||||
let valueKey = this.relatedTelemetry[key].historical.valueKey;
|
||||
let valuesOnTelemetry = this.relatedTelemetry[key].historicalValuesOnTelemetry;
|
||||
|
||||
if (valuesOnTelemetry) {
|
||||
mostRecent = targetDatum[valueKey];
|
||||
|
||||
if (mostRecent) {
|
||||
return mostRecent;
|
||||
} else {
|
||||
console.warn(`Related Telemetry for ${key} does NOT exist on this telemetry datum as configuration implied.`);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mostRecent = await this.relatedTelemetry[key].requestLatestFor(targetDatum);
|
||||
|
||||
return mostRecent[valueKey];
|
||||
},
|
||||
// will subscribe to data for this key if not already done
|
||||
subscribeToDataForKey(key) {
|
||||
if (this.relatedTelemetry[key].isSubscribed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.relatedTelemetry[key].realtimeDomainObject) {
|
||||
this.relatedTelemetry[key].unsubscribe = this.openmct.telemetry.subscribe(
|
||||
this.relatedTelemetry[key].realtimeDomainObject, datum => {
|
||||
this.relatedTelemetry[key].listeners.forEach(callback => {
|
||||
callback(datum);
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
this.relatedTelemetry[key].isSubscribed = true;
|
||||
}
|
||||
},
|
||||
async updateRelatedTelemetryForFocusedImage() {
|
||||
if (!this.hasRelatedTelemetry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const image = this.imageHistory[this.focusedImageIndex];
|
||||
|
||||
// set data ON image telemetry as well as in focusedImageRelatedData
|
||||
for (let key of this.relatedTelemetry.keys) {
|
||||
if (this.relatedTelemetry[key] && this.relatedTelemetry[key].historical) {
|
||||
let valuesOnTelemetry = this.relatedTelemetry[key].historicalValuesOnTelemetry;
|
||||
let value = await this.getMostRecentRelatedTelemetry(key, this.focusedImage);
|
||||
|
||||
if (!valuesOnTelemetry) {
|
||||
image[key] = value; // manually add to telemetry
|
||||
}
|
||||
|
||||
this.$set(this.focusedImageRelatedData, key, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
focusElement() {
|
||||
this.$el.focus();
|
||||
},
|
||||
@@ -509,6 +788,25 @@ export default {
|
||||
},
|
||||
isLeftOrRightArrowKey(keyCode) {
|
||||
return [ARROW_RIGHT, ARROW_LEFT].includes(keyCode);
|
||||
},
|
||||
getImageNaturalDimensions() {
|
||||
this.focusedImageNaturalAspectRatio = undefined;
|
||||
|
||||
const img = this.$refs.focusedImage;
|
||||
|
||||
// TODO - should probably cache this
|
||||
img.addEventListener('load', () => {
|
||||
this.focusedImageNaturalAspectRatio = img.naturalWidth / img.naturalHeight;
|
||||
}, { once: true });
|
||||
},
|
||||
pollResizeImageContainer() {
|
||||
if (this.$refs.focusedImage.clientWidth !== this.imageContainerWidth) {
|
||||
this.imageContainerWidth = this.$refs.focusedImage.clientWidth;
|
||||
}
|
||||
|
||||
if (this.$refs.focusedImage.clientHeight !== this.imageContainerHeight) {
|
||||
this.imageContainerHeight = this.$refs.focusedImage.clientHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
background-color: $colorPlotBg;
|
||||
border: 1px solid transparent;
|
||||
flex: 1 1 auto;
|
||||
height: 0;
|
||||
|
||||
&.unnsynced{
|
||||
@include sUnsynced();
|
||||
@@ -30,10 +31,9 @@
|
||||
}
|
||||
|
||||
&__image {
|
||||
@include abs(); // Safari fix
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is 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.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import ImageryViewProvider from './ImageryViewProvider';
|
||||
|
||||
export default function () {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
@import "../plugins/folderView/components/list-item.scss";
|
||||
@import "../plugins/folderView/components/list-view.scss";
|
||||
@import "../plugins/imagery/components/imagery-view-layout.scss";
|
||||
@import "../plugins/imagery/components/Compass/compass.scss";
|
||||
@import "../plugins/telemetryTable/components/table-row.scss";
|
||||
@import "../plugins/telemetryTable/components/table-footer-indicator.scss";
|
||||
@import "../plugins/tabs/components/tabs.scss";
|
||||
|
||||
Reference in New Issue
Block a user