componentize all the things (also: icons)
@ -1,175 +1,83 @@
|
||||
<template>
|
||||
<header>
|
||||
<div class="headline">
|
||||
<h1>Starsy</h1>
|
||||
<p>Starsystem<br/>Generator</p>
|
||||
</div>
|
||||
<div class="options">
|
||||
<label>
|
||||
Title Font
|
||||
<select v-model="selectedFont">
|
||||
<option v-for="f in labelFonts">{{ f }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Color Theme
|
||||
<select v-model="selectedTheme" @change="setTheme">
|
||||
<option v-for="t in themes">{{ t }}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<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" />
|
||||
<circle id="star" :r="star.radius" :cx="starCX" cy="150" />
|
||||
|
||||
<g class="object" :id="o.name" v-for="o in objects">
|
||||
<g class="rings" v-for="i in o.rings">
|
||||
<circle :r="o.radius - 5 + 2*i" :cx="o.distance" cy="150" />
|
||||
</g>
|
||||
|
||||
<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" />
|
||||
<line v-if="o.satellites.length" :x1="o.distance" y1="150" :x2="o.distance" :y2="150 + o.radius + 10*o.satellites.length" />
|
||||
|
||||
<g class="satellite" v-for="m,i in o.satellites">
|
||||
<rect v-if="m.type === 'station'" class="station" :x="o.distance - 2" :y="158 + o.radius + 10*i" width="4" height="4" />
|
||||
<circle v-else :r="m.radius" :cx="o.distance" :cy="160 + o.radius + 10*i" />
|
||||
<text :x="o.distance + 5" :y="162 + o.radius + 10*i">{{ m.name }}</text>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
<text id="designation" :class="`title-${selectedFont}`" x="980" y="30">{{ star.designation }}</text>
|
||||
</svg>
|
||||
<Headline
|
||||
v-bind="{ labelFonts, themes }"
|
||||
@select:font="setFont($event)"
|
||||
@select:theme="setTheme($event)"
|
||||
/>
|
||||
<SystemDiagram v-bind="{ star, objects }" />
|
||||
|
||||
<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">
|
||||
<header>
|
||||
<h2><input type="text" v-model="o.name" @blur="checkName(o)"/> settings</h2>
|
||||
<p>Distance Δ: {{ o.distance }}</p>
|
||||
<p>Radius r: {{ o.radius }}</p>
|
||||
<p>Rings ⬭: {{ o.rings > 0 ? o.rings : 'none' }}</p>
|
||||
<p>Satellites: {{ listSatellites(o) }}</p>
|
||||
</header>
|
||||
<label>
|
||||
<span>Δ</span>
|
||||
<input class="planet-distance" type="range" min="50" max="1000" v-model="o.distance" />
|
||||
</label>
|
||||
<label>
|
||||
<span>r</span>
|
||||
<input class="planet-radius" type="range" min="1" max="125" v-model="o.radius" />
|
||||
</label>
|
||||
<label>
|
||||
<span>⬭</span>
|
||||
<input class="planet-rings" type="number" min="0" max="15" v-model="o.rings" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button>add object</button>
|
||||
</menu>
|
||||
<ObjectSettings v-model:object="selectedObject" v-if="selectedObject" />
|
||||
<Tips>
|
||||
<li>Edit planets by clicking directly inside the graphic or in the table below.</li>
|
||||
<li>Drag planets around to change their distance.</li>
|
||||
<li>Use the scrollwheel to change their size.</li>
|
||||
<li>To change satellites, click their respective buttons in the planet dialog.</li>
|
||||
<li>You can also drag satellite buttons around to reorder them.</li>
|
||||
</Tips>
|
||||
<SystemSettings v-model:designation="star.designation" v-model:radius="star.radius" />
|
||||
<ObjectList v-bind="{ objects, editObject, deleteObject }" />
|
||||
</section>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import steepCurve from './steep-curve'
|
||||
import { ref, reactive } from 'vue'
|
||||
import exampleData from './example-data'
|
||||
import Headline from './components/Headline.vue'
|
||||
import SystemDiagram from './components/SystemDiagram.vue'
|
||||
import Tips from './components/Tips.vue'
|
||||
import SystemSettings from './components/SystemSettings.vue'
|
||||
import ObjectList from './components/ObjectList.vue'
|
||||
import ObjectSettings from './components/ObjectSettings.vue'
|
||||
|
||||
const star = reactive({
|
||||
designation: 'Sol',
|
||||
radius: 400,
|
||||
})
|
||||
|
||||
const starCX = computed(() => -1 * star.radius * steepCurve(star.radius, 50, 0.955))
|
||||
|
||||
const objects = ref([
|
||||
{ type: 'planet', name: 'Mercury', radius: 1, distance: 100, satellites: [], rings: 0 },
|
||||
{ type: 'planet', name: 'Venus', radius: 4, distance: 120, satellites: [], rings: 0 },
|
||||
{ type: 'planet', name: 'Terra', radius: 4, distance: 140, satellites: [
|
||||
{ name: 'ISS', type: 'station' },
|
||||
{ name: 'Luna', radius: 2 },
|
||||
], rings: 0 },
|
||||
{ type: 'planet', name: 'Mars', radius: 2, distance: 160, satellites: [
|
||||
{ name: 'MTO', type: 'station' },
|
||||
{ name: 'Phobos', radius: 1 },
|
||||
{ name: 'Daimos', radius: 1 },
|
||||
], rings: 0 },
|
||||
{ type: 'planet', name: 'Jupiter', radius: 40, distance: 260, satellites: [
|
||||
{ name: 'Io', radius: 2 },
|
||||
{ name: 'Europa', radius: 2 },
|
||||
{ name: 'Ganymede', radius: 4 },
|
||||
{ name: 'Callisto', radius: 3 },
|
||||
], rings: 1 },
|
||||
{ type: 'planet', name: 'Saturn', radius: 36, distance: 410, satellites: [
|
||||
{ name: 'Mimas', radius: 1 },
|
||||
{ name: 'Enceladus', radius: 1 },
|
||||
{ name: 'Tethys', radius: 1 },
|
||||
{ name: 'Dione', radius: 1 },
|
||||
{ name: 'Rhea', radius: 1 },
|
||||
{ name: 'Titan', radius: 3 },
|
||||
{ name: 'Iapetus', radius: 1 },
|
||||
], rings: 5 },
|
||||
{ type: 'planet', name: 'Uranus', radius: 16, distance: 680, satellites: [
|
||||
{ name: 'Miranda', radius: 1 },
|
||||
{ name: 'Ariel', radius: 1 },
|
||||
{ name: 'Umbriel', radius: 1 },
|
||||
{ name: 'Titania', radius: 1 },
|
||||
{ name: 'Oberon', radius: 1 },
|
||||
], rings: 2 },
|
||||
{ type: 'planet', name: 'Neptune', radius: 15, distance: 950, satellites: [
|
||||
{ name: 'Triton', radius: 1 },
|
||||
], rings: 0 },
|
||||
])
|
||||
const objects = ref(exampleData)
|
||||
const labelFonts = ['xolonium', 'douar', 'lack']
|
||||
const themes = ['default', 'retro', 'inverse', 'paper']
|
||||
|
||||
const selectedObject = ref(null)
|
||||
|
||||
function toggleObject (obj) {
|
||||
selectedObject.value = selectedObject.value === obj ? null : obj
|
||||
function editObject (obj) {
|
||||
selectedObject.value = obj
|
||||
}
|
||||
function checkName (obj) {
|
||||
if (!obj.name.trim().length) {
|
||||
const index = objects.value.indexOf(obj)
|
||||
obj.name = `${star.designation}-${index}`
|
||||
}
|
||||
|
||||
function deleteObject (obj) {
|
||||
console.log('delete object not yet implemented')
|
||||
}
|
||||
|
||||
const labelFonts = ['douar', 'lack', 'xolonium']
|
||||
const selectedFont = ref('xolonium')
|
||||
function autoName (obj) {
|
||||
const index = objects.value.indexOf(obj)
|
||||
return `${star.designation}-${index}`
|
||||
}
|
||||
|
||||
const themes = ['default', 'retro', 'inverse', 'paper']
|
||||
const selectedTheme = ref('default')
|
||||
function setTheme (theme) {
|
||||
const classes = document.body.className.split(' ')
|
||||
const currentTheme = classes.find(c => c.startsWith('theme-'))
|
||||
const newTheme = `theme-${theme}`
|
||||
|
||||
function setTheme () {
|
||||
document.body.className = `theme-${selectedTheme.value}`
|
||||
if (currentTheme) {
|
||||
document.body.classList.replace(currentTheme, newTheme)
|
||||
} else {
|
||||
document.body.classList.add(newTheme)
|
||||
}
|
||||
}
|
||||
|
||||
function listSatellites (obj) {
|
||||
if (!obj.satellites || !obj.satellites.length) return 'none'
|
||||
return obj.satellites.reduce((acc, satellite) => {
|
||||
let s = satellite.name
|
||||
if (satellite.type) s += ` (${satellite.type})`
|
||||
acc.push(s)
|
||||
return acc
|
||||
}, []).join(', ')
|
||||
function setFont (font) {
|
||||
const classes = document.body.className.split(' ')
|
||||
const currentFont = classes.find(c => c.startsWith('title-'))
|
||||
const newFont = `title-${font}`
|
||||
|
||||
if (currentFont) {
|
||||
document.body.classList.replace(currentFont, newFont)
|
||||
} else {
|
||||
document.body.classList.add(newFont)
|
||||
}
|
||||
}
|
||||
setTheme()
|
||||
|
||||
setTheme(themes[0])
|
||||
setFont(labelFonts[0])
|
||||
</script>
|
||||
|
After Width: | Height: | Size: 801 B |
After Width: | Height: | Size: 763 B |
After Width: | Height: | Size: 958 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 717 B |
After Width: | Height: | Size: 691 B |
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<button title="delete object" @click="$emit('click', $event)">
|
||||
<img src="../assets/delete.png" alt="delete icon" />
|
||||
</button>
|
||||
</template>
|
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<header>
|
||||
<div class="headline">
|
||||
<h1>Starsy</h1>
|
||||
<p>Starsystem<br/>Generator</p>
|
||||
</div>
|
||||
<div class="options">
|
||||
<label>
|
||||
Title Font
|
||||
<select v-model="selectedFont" @change="$emit('select:font', selectedFont)">
|
||||
<option v-for="f in labelFonts">{{ f }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Color Theme
|
||||
<select v-model="selectedTheme" @change="$emit('select:theme', selectedTheme)">
|
||||
<option v-for="t in themes">{{ t }}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
labelFonts: Array,
|
||||
themes: Array,
|
||||
})
|
||||
|
||||
const selectedFont = ref(props.labelFonts[0])
|
||||
const selectedTheme = ref('default')
|
||||
</script>
|
@ -1,21 +0,0 @@
|
||||
<template>
|
||||
<g class="planetary">
|
||||
</g>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
name: String,
|
||||
size: Number,
|
||||
type: String, // TODO
|
||||
moons: Object, // moons: { name, size as fraction of planet size }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<table id="object-list">
|
||||
<tr>
|
||||
<th scope="col" v-for="col in columns">{{ col }}</th>
|
||||
<th scope="col">actions</th>
|
||||
</tr>
|
||||
<tr v-for="o in objects">
|
||||
<td v-for="value in values">
|
||||
<div class="cell">{{ o[value] }}</div>
|
||||
</td>
|
||||
<td><div class="cell">{{ o.satellites.length }}</div></td>
|
||||
<td><div class="cell">
|
||||
<button class="settings" @click="editObject(o)"> </button>
|
||||
<button class="delete" @click="deleteObject(o)"> </button>
|
||||
</div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
objects: Array,
|
||||
editObject: Function,
|
||||
deleteObject: Function,
|
||||
})
|
||||
|
||||
const columns = ['Δ', 'Name', 'Type', 'Radius', 'Rings', 'Satellites']
|
||||
const values = ['distance', 'name', 'type', 'radius', 'rings']
|
||||
</script>
|
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="object-settings">
|
||||
<header>
|
||||
<h2><input type="text" v-model="name" @blur="checkName"/></h2>
|
||||
<p>
|
||||
Distance Δ:
|
||||
<input type="number" min="50" max="1000" v-model="distance" />
|
||||
</p>
|
||||
<p>
|
||||
Radius r:
|
||||
<input type="number" min="1" max="125" v-model="radius" />
|
||||
</p>
|
||||
<p>
|
||||
Rings:
|
||||
<input type="number" min="0" max="15" v-model="rings" />
|
||||
</p>
|
||||
</header>
|
||||
<div class="satellite-list">
|
||||
Satellites:
|
||||
<button class="satellite" v-for="satellite in satellites">
|
||||
{{ satellite.name }}
|
||||
<template v-if="satellite.type">({{ satellite.type }})</template>
|
||||
</button>
|
||||
<button class="add"> </button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
object: Object,
|
||||
})
|
||||
const emit = defineEmits([
|
||||
'update:object',
|
||||
])
|
||||
|
||||
const tipsShown = ref(true)
|
||||
|
||||
const distance = ref(props.object.distance)
|
||||
const name = ref(props.object.name)
|
||||
const type = ref(props.object.type)
|
||||
const radius = ref(props.object.radius)
|
||||
const rings = ref(props.object.rings)
|
||||
const satellites = ref(props.object.satellites)
|
||||
|
||||
const satellitesList = computed(() => {
|
||||
if (!satellites.value || !satellites.value.length) return 'none'
|
||||
return satellites.value.reduce((acc, satellite) => {
|
||||
let s = satellite.name
|
||||
if (satellite.type) s += ` (${satellite.type})`
|
||||
acc.push(s)
|
||||
return acc
|
||||
}, []).join(', ')
|
||||
})
|
||||
|
||||
function update (target, value) {
|
||||
if (target === 'radius') value = parseInt(value)
|
||||
emit(`update:${target}`, value)
|
||||
}
|
||||
</script>
|
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<button title="settings" @click="$emit('click', $event)">
|
||||
<img src="../assets/change.png" alt="settings icon" />
|
||||
</button>
|
||||
</template>
|
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<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" />
|
||||
<circle id="star" :r="star.radius" :cx="starCX" cy="150" />
|
||||
|
||||
<g class="object" :id="o.name" v-for="o in objects">
|
||||
<g class="rings" v-for="i in o.rings">
|
||||
<circle :r="o.radius - 5 + 2*i" :cx="o.distance" cy="150" />
|
||||
</g>
|
||||
|
||||
<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" />
|
||||
<line v-if="o.satellites.length" :x1="o.distance" y1="150" :x2="o.distance" :y2="150 + o.radius + 10*o.satellites.length" />
|
||||
|
||||
<g class="satellite" v-for="m,i in o.satellites">
|
||||
<rect v-if="m.type === 'station'" class="station" :x="o.distance - 2" :y="158 + o.radius + 10*i" width="4" height="4" />
|
||||
<circle v-else :r="m.radius" :cx="o.distance" :cy="160 + o.radius + 10*i" />
|
||||
<text :x="o.distance + 5" :y="162 + o.radius + 10*i">{{ m.name }}</text>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
<text id="designation" x="980" y="30">{{ star.designation }}</text>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import steepCurve from '../steep-curve'
|
||||
|
||||
const props = defineProps({
|
||||
star: Object,
|
||||
objects: Array,
|
||||
})
|
||||
|
||||
const starCX = computed(() => {
|
||||
const r = props.star.radius
|
||||
return -1 * r * steepCurve(r, 50, 0.955)
|
||||
})
|
||||
</script>
|
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<header>
|
||||
<h1>Star System Parameters</h1>
|
||||
<menu id="system-settings">
|
||||
<label>
|
||||
Name
|
||||
<input type="text" :value="designation" @input="update('designation', $event.target.value)" />
|
||||
</label>
|
||||
<label>
|
||||
Star Size
|
||||
<input type="range" min="50" max="1500" :value="radius" @input="update('radius', $event.target.value)" />
|
||||
({{ radius }})
|
||||
</label>
|
||||
</menu>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
designation: String,
|
||||
radius: Number,
|
||||
})
|
||||
const emit = defineEmits([
|
||||
'update:designation',
|
||||
'update:radius',
|
||||
])
|
||||
|
||||
function update (target, value) {
|
||||
if (target === 'radius') value = parseInt(value)
|
||||
emit(`update:${target}`, value)
|
||||
}
|
||||
</script>
|
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<ul class="tip">
|
||||
<header>
|
||||
Tips:
|
||||
<button @click="tipsShown = !tipsShown">{{ tipsShown ? 'close' : 'expand' }}</button>
|
||||
</header>
|
||||
<template v-if="tipsShown">
|
||||
<slot />
|
||||
</template>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const tipsShown = ref(true)
|
||||
</script>
|
@ -0,0 +1,38 @@
|
||||
export default [
|
||||
{ type: 'planet', name: 'Mercury', radius: 1, distance: 100, satellites: [], rings: 0 },
|
||||
{ type: 'planet', name: 'Venus', radius: 4, distance: 120, satellites: [], rings: 0 },
|
||||
{ type: 'planet', name: 'Terra', radius: 4, distance: 140, satellites: [
|
||||
{ name: 'ISS', type: 'station' },
|
||||
{ name: 'Luna', radius: 2 },
|
||||
], rings: 0 },
|
||||
{ type: 'planet', name: 'Mars', radius: 2, distance: 160, satellites: [
|
||||
{ name: 'MTO', type: 'station' },
|
||||
{ name: 'Phobos', radius: 1 },
|
||||
{ name: 'Daimos', radius: 1 },
|
||||
], rings: 0 },
|
||||
{ type: 'planet', name: 'Jupiter', radius: 40, distance: 260, satellites: [
|
||||
{ name: 'Io', radius: 2 },
|
||||
{ name: 'Europa', radius: 2 },
|
||||
{ name: 'Ganymede', radius: 4 },
|
||||
{ name: 'Callisto', radius: 3 },
|
||||
], rings: 1 },
|
||||
{ type: 'planet', name: 'Saturn', radius: 36, distance: 410, satellites: [
|
||||
{ name: 'Mimas', radius: 1 },
|
||||
{ name: 'Enceladus', radius: 1 },
|
||||
{ name: 'Tethys', radius: 1 },
|
||||
{ name: 'Dione', radius: 1 },
|
||||
{ name: 'Rhea', radius: 1 },
|
||||
{ name: 'Titan', radius: 3 },
|
||||
{ name: 'Iapetus', radius: 1 },
|
||||
], rings: 5 },
|
||||
{ type: 'planet', name: 'Uranus', radius: 16, distance: 680, satellites: [
|
||||
{ name: 'Miranda', radius: 1 },
|
||||
{ name: 'Ariel', radius: 1 },
|
||||
{ name: 'Umbriel', radius: 1 },
|
||||
{ name: 'Titania', radius: 1 },
|
||||
{ name: 'Oberon', radius: 1 },
|
||||
], rings: 2 },
|
||||
{ type: 'planet', name: 'Neptune', radius: 15, distance: 950, satellites: [
|
||||
{ name: 'Triton', radius: 1 },
|
||||
], rings: 0 },
|
||||
]
|