star system settings dialog
This commit is contained in:
parent
58712b0edf
commit
f7abc517eb
3 changed files with 140 additions and 13 deletions
67
src/App.vue
67
src/App.vue
|
@ -22,14 +22,14 @@
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1000 300">
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1000 300">
|
||||||
<line id="axis" x1="0" y1="150" x2="1000" y2="150" />
|
<line id="axis" x1="0" y1="150" x2="1000" y2="150" />
|
||||||
<circle id="star" r="400" cx="-370" cy="150" />
|
<circle id="star" :r="star.radius" :cx="starCX" cy="150" />
|
||||||
|
|
||||||
<g class="object" :id="o.name" v-for="o in objects">
|
<g class="object" :id="o.name" v-for="o in objects">
|
||||||
<g class="rings" v-for="i in o.rings">
|
<g class="rings" v-for="i in o.rings">
|
||||||
<circle :r="o.radius - 5 + 2*i" :cx="o.distance" cy="150" />
|
<circle :r="o.radius - 5 + 2*i" :cx="o.distance" cy="150" />
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<text :x="o.distance" :y="140 - o.radius">{{ o.name }}</text>
|
<text :class="{ tilted: o.radius < 10 }" :x="o.distance" :y="140 - o.radius">{{ o.name }}</text>
|
||||||
<circle v-if="o.type === 'planet'" :r="o.radius" :cx="o.distance" cy="150" />
|
<circle v-if="o.type === 'planet'" :r="o.radius" :cx="o.distance" cy="150" />
|
||||||
<line v-if="o.moons.length" :x1="o.distance" y1="150" :x2="o.distance" :y2="150 + o.radius + 10*o.moons.length" />
|
<line v-if="o.moons.length" :x1="o.distance" y1="150" :x2="o.distance" :y2="150 + o.radius + 10*o.moons.length" />
|
||||||
|
|
||||||
|
@ -41,20 +41,52 @@
|
||||||
|
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<text id="label" :class="`title-${selectedFont}`" x="980" y="30">{{ label }}</text>
|
<text id="designation" :class="`title-${selectedFont}`" x="980" y="30">{{ star.designation }}</text>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<div id="settings">
|
<section id="settings">
|
||||||
|
<header>
|
||||||
|
<h1>Star System Parameters</h1>
|
||||||
|
<menu id="system-settings">
|
||||||
|
<label>
|
||||||
|
Name
|
||||||
|
<input type="text" v-model="star.designation" />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Star Size
|
||||||
|
<input type="range" min="50" max="1500" v-model="star.radius" />
|
||||||
|
({{ star.radius }})
|
||||||
|
</label>
|
||||||
|
</menu>
|
||||||
|
</header>
|
||||||
|
<menu id="object-list">
|
||||||
|
<div class="menu-item" :class="{ open: selectedObject === o }" v-for="o in objects">
|
||||||
|
<label @click="toggleObject(o)">{{ o.name }}</label>
|
||||||
|
<div class="object-settings">
|
||||||
|
<h2>{{ o.name }} settings</h2>
|
||||||
|
<p>Distance from central star: {{ o.distance }}</p>
|
||||||
|
<p>Radius: {{ o.radius }}</p>
|
||||||
|
<p>Moons and Stations: {{ listMoons(o) }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<button>add object</button>
|
||||||
|
</menu>
|
||||||
|
</section>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref, reactive, computed } from 'vue'
|
||||||
|
import steepCurve from './steep-curve'
|
||||||
|
|
||||||
const label = "Sol"
|
const star = reactive({
|
||||||
|
designation: 'Sol',
|
||||||
|
radius: 400,
|
||||||
|
})
|
||||||
|
|
||||||
const objects = [
|
const starCX = computed(() => -1 * star.radius * steepCurve(star.radius, 50, 0.955))
|
||||||
|
|
||||||
|
const objects = ref([
|
||||||
{ type: 'planet', name: 'Mercury', radius: 1, distance: 100, moons: [], rings: [] },
|
{ type: 'planet', name: 'Mercury', radius: 1, distance: 100, moons: [], rings: [] },
|
||||||
{ type: 'planet', name: 'Venus', radius: 4, distance: 120, moons: [], rings: [] },
|
{ type: 'planet', name: 'Venus', radius: 4, distance: 120, moons: [], rings: [] },
|
||||||
{ type: 'planet', name: 'Terra', radius: 4, distance: 140, moons: [
|
{ type: 'planet', name: 'Terra', radius: 4, distance: 140, moons: [
|
||||||
|
@ -91,10 +123,16 @@ const objects = [
|
||||||
{ type: 'planet', name: 'Neptune', radius: 15, distance: 950, moons: [
|
{ type: 'planet', name: 'Neptune', radius: 15, distance: 950, moons: [
|
||||||
{ name: 'Triton', radius: 1 },
|
{ name: 'Triton', radius: 1 },
|
||||||
], rings: [] },
|
], rings: [] },
|
||||||
]
|
])
|
||||||
|
|
||||||
|
const selectedObject = ref(null)
|
||||||
|
|
||||||
|
function toggleObject (obj) {
|
||||||
|
selectedObject.value = selectedObject.value === obj ? null : obj
|
||||||
|
}
|
||||||
|
|
||||||
const labelFonts = ['douar', 'lack', 'xolonium']
|
const labelFonts = ['douar', 'lack', 'xolonium']
|
||||||
const selectedFont = ref('lack')
|
const selectedFont = ref('xolonium')
|
||||||
|
|
||||||
const themes = ['default', 'retro', 'inverse', 'paper']
|
const themes = ['default', 'retro', 'inverse', 'paper']
|
||||||
const selectedTheme = ref('default')
|
const selectedTheme = ref('default')
|
||||||
|
@ -102,4 +140,15 @@ const selectedTheme = ref('default')
|
||||||
function setTheme () {
|
function setTheme () {
|
||||||
document.body.className = `theme-${selectedTheme.value}`
|
document.body.className = `theme-${selectedTheme.value}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function listMoons (obj) {
|
||||||
|
if (!obj.moons || !obj.moons.length) return 'none'
|
||||||
|
return obj.moons.reduce((acc, moon) => {
|
||||||
|
let s = moon.name
|
||||||
|
if (moon.type) s += ` (${moon.type})`
|
||||||
|
acc.push(s)
|
||||||
|
return acc
|
||||||
|
}, []).join('; ')
|
||||||
|
}
|
||||||
|
setTheme()
|
||||||
</script>
|
</script>
|
||||||
|
|
76
src/app.css
76
src/app.css
|
@ -22,9 +22,12 @@
|
||||||
:root {
|
:root {
|
||||||
--bg-graphic: #000;
|
--bg-graphic: #000;
|
||||||
--fg-graphic: #FFF;
|
--fg-graphic: #FFF;
|
||||||
|
--hl-graphic: #FFB;
|
||||||
--bg-app: #333;
|
--bg-app: #333;
|
||||||
--fg-app: #EEE;
|
--fg-app: #EEE;
|
||||||
--fill-star: #FFB;
|
--fill-star: #FFB;
|
||||||
|
--bg-settings: var(--bg-graphic);
|
||||||
|
--fg-settings: var(--fg-graphic);
|
||||||
--title-font: xolonium;
|
--title-font: xolonium;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,37 +48,77 @@ body.theme-retro {
|
||||||
--fg-app: #E4DCB5;
|
--fg-app: #E4DCB5;
|
||||||
--bg-graphic: #E4DCB5;
|
--bg-graphic: #E4DCB5;
|
||||||
--fg-graphic: #4B4839;
|
--fg-graphic: #4B4839;
|
||||||
|
--hl-graphic: #222;
|
||||||
--fill-star: var(--fg-graphic);
|
--fill-star: var(--fg-graphic);
|
||||||
|
--bg-settings: var(--bg-graphic);
|
||||||
|
--fg-settings: var(--fg-graphic);
|
||||||
}
|
}
|
||||||
body.theme-inverse {
|
body.theme-inverse {
|
||||||
--bg-app: #333;
|
--bg-app: #333;
|
||||||
--fg-app: #E4DCB5;
|
--fg-app: #E4DCB5;
|
||||||
--bg-graphic: #4B4839;
|
--bg-graphic: #4B4839;
|
||||||
--fg-graphic: #E4DCB5;
|
--fg-graphic: #E4DCB5;
|
||||||
|
--hl-graphic: #FF0;
|
||||||
--fill-star: var(--fg-graphic);
|
--fill-star: var(--fg-graphic);
|
||||||
|
--bg-settings: var(--bg-graphic);
|
||||||
|
--fg-settings: var(--fg-graphic);
|
||||||
}
|
}
|
||||||
body.theme-paper {
|
body.theme-paper {
|
||||||
--bg-app: #FFF;
|
--bg-app: #FFF;
|
||||||
--fg-app: #000;
|
--fg-app: #000;
|
||||||
--bg-graphic: #FFF;
|
--bg-graphic: #FFF;
|
||||||
--fg-graphic: #000;
|
--fg-graphic: #000;
|
||||||
|
--hl-graphic: #555;
|
||||||
--fill-star: var(--fg-graphic);
|
--fill-star: var(--fg-graphic);
|
||||||
|
--bg-settings: #DDD;
|
||||||
|
--fg-settings: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-douar { font-family: 'douar'; }
|
.title-douar { font-family: 'douar'; }
|
||||||
.title-lack { font-family: 'lack'; }
|
.title-lack { font-family: 'lack'; }
|
||||||
.title-xolonium { font-family: 'xolonium'; }
|
.title-xolonium { font-family: 'xolonium'; }
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
.menu-item > label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.menu-item > label::before {
|
||||||
|
content: "▸ ";
|
||||||
|
}
|
||||||
|
.menu-item.open > label::before {
|
||||||
|
content: "▾ ";
|
||||||
|
}
|
||||||
|
.menu-item.open {
|
||||||
|
background: var(--bg-settings);
|
||||||
|
color: var(--fg-settings);
|
||||||
|
}
|
||||||
|
.menu-item > .object-settings {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100vw - 4em);
|
||||||
|
margin-top: 1em;
|
||||||
|
padding: 1em 2em;
|
||||||
|
background: var(--bg-graphic);
|
||||||
|
}
|
||||||
|
.menu-item.open > .object-settings {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
svg { background: var(--bg-graphic); }
|
svg { background: var(--bg-graphic); }
|
||||||
line { stroke: var(--fg-graphic); }
|
line { stroke: var(--fg-graphic); }
|
||||||
text.tilted { transform: rotate(-45deg) translate(0, 100%); transform-origin: left top; transform-box: fill-box; }
|
text.tilted { transform: rotate(-45deg) translate(0, 100%); transform-origin: left top; transform-box: fill-box; }
|
||||||
#axis { stroke-width: 1; }
|
#axis { stroke-width: 1; }
|
||||||
#label { fill: var(--fg-graphic); text-anchor: end; }
|
#designation { fill: var(--fg-graphic); text-anchor: end; }
|
||||||
#star { fill: var(--fill-star); }
|
#star { fill: var(--fill-star); }
|
||||||
.object { fill: var(--fg-graphic); text-anchor: middle; font-size: .6em; }
|
.object { fill: var(--fg-graphic); text-anchor: middle; font-size: .6em; cursor: pointer; }
|
||||||
.moon { text-anchor: start; font-size: .7em; }
|
|
||||||
.object > line { stroke-width: .5; }
|
.object > line { stroke-width: .5; }
|
||||||
|
.moon { text-anchor: start; font-size: .7em; }
|
||||||
.rings { stroke: var(--fg-graphic); fill: none; transform: skew(-45deg); transform-origin: 50%; }
|
.rings { stroke: var(--fg-graphic); fill: none; transform: skew(-45deg); transform-origin: 50%; }
|
||||||
|
.object:hover { fill: var(--hl-graphic); }
|
||||||
|
.object:hover > line, .object:hover .rings { stroke: var(--hl-graphic); }
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-family: xolonium;
|
font-family: xolonium;
|
||||||
|
@ -98,3 +141,30 @@ h1 {
|
||||||
#settings {
|
#settings {
|
||||||
padding: 1em 2em;
|
padding: 1em 2em;
|
||||||
}
|
}
|
||||||
|
#settings > header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
#settings > header > h1 {
|
||||||
|
min-width: 330px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#system-settings, #object-list {
|
||||||
|
display: flex;
|
||||||
|
width: calc(100vw - 4em);
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#system-settings > label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 300px;
|
||||||
|
margin: 0 1em;
|
||||||
|
}
|
||||||
|
#system-settings input {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
#system-settings input[type="text"] {
|
||||||
|
margin-left: 1em;
|
||||||
|
padding: .5em 1em .4em;
|
||||||
|
}
|
||||||
|
|
8
src/steep-curve.js
Normal file
8
src/steep-curve.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Thank you Ingo for your tremendous help with this one.
|
||||||
|
|
||||||
|
// This function returns a steep curve from [minX,0] to [infinity,maxY]
|
||||||
|
// inc is tuned for x-values between minX and minX+100 describing a gentle curve
|
||||||
|
export default function steepCurve (x, minX, maxY, inc=0.01) {
|
||||||
|
// f(x) = maxY * (1 - e^(-(inc*x)+minX*inc))
|
||||||
|
return maxY * (1 - Math.E ** (-(inc*x) + minX*inc))
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue