Skip to content

Commit

Permalink
feat(export): add legends to export
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelMakesGames committed Sep 9, 2024
1 parent b342e58 commit 5efb1c7
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 28 deletions.
86 changes: 65 additions & 21 deletions src/renderer/src/lib/ExportModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
import { onDestroy } from 'svelte';
import { t } from '../intl';
import type { GalacticObject, GameState } from './GameState';
import convertBlobToDataUrl from './convertBlobToDataUrl';
import convertSvgToPng from './convertSvgToPng';
import Legend from './map/Legend.svelte';
import type { MapData } from './map/data/processMapData';
import { getBackgroundColor, getFillColorAttributes, resolveColor } from './map/mapUtils';
import SolarSystemMap from './map/solarSystemMap/SolarSystemMap.svelte';
import processStarScape from './map/starScape/renderStarScape';
import { mapSettings } from './settings';
import { mapSettings, type MapSettings } from './settings';
import stellarMapsApi from './stellarMapsApi';
import { toastError } from './utils';
Expand All @@ -40,7 +42,18 @@
},
})
: null;
onDestroy(() => solarSystemMap?.$destroy());
const legendTarget = document.createElement('div');
const legend = new Legend({
target: legendTarget,
props: {
colors: colors,
data: mapData,
},
});
onDestroy(() => {
solarSystemMap?.$destroy();
legend?.$destroy();
});
const defaultExportSettings = {
lockAspectRatio: true,
Expand Down Expand Up @@ -85,6 +98,15 @@
? Math.max(mapHeight, 500 - viewBoxTop)
: Math.max(1000, 500 + mapTop + mapHeight);
function hasBackgroundImage(mapSettings: MapSettings) {
return (
mapSettings.starScapeDust ||
mapSettings.starScapeCore ||
mapSettings.starScapeNebula ||
mapSettings.starScapeStars
);
}
function closeAndSaveSettings() {
exportSettings.set({
lockAspectRatio,
Expand Down Expand Up @@ -117,23 +139,34 @@
}
async function exportPng() {
const backgroundImageUrl = openedSystem
? undefined
: await processStarScape(
gameState,
$mapSettings,
colors,
{
left: mapLeft,
top: mapTop,
width: mapWidth,
height: mapHeight,
},
{
width: imageWidth,
height: imageHeight,
},
);
const backgroundImageUrl =
openedSystem || !hasBackgroundImage($mapSettings)
? undefined
: await processStarScape(
gameState,
$mapSettings,
colors,
{
left: mapLeft,
top: mapTop,
width: mapWidth,
height: mapHeight,
},
{
width: imageWidth,
height: imageHeight,
},
);
const legendImageUrl = await convertSvgToPng(legendTarget.firstChild as SVGElement, {
left: 0,
top: 0,
width: (1000 * imageWidth) / imageHeight,
height: 1000,
outputWidth: imageWidth,
outputHeight: imageHeight,
}).then(convertBlobToDataUrl);
const buffer = await convertSvgToPng(
solarSystemMap ? (solarSystemMapTarget.firstChild as SVGElement) : galaxyMapSvg,
{
Expand All @@ -144,6 +177,7 @@
outputWidth: imageWidth,
outputHeight: imageHeight,
backgroundImageUrl,
foregroundImageUrl: openedSystem ? undefined : legendImageUrl,
backgroundColor: getBackgroundColor(colors, $mapSettings),
},
).then((blob) => blob.arrayBuffer());
Expand Down Expand Up @@ -185,7 +219,7 @@
svgToExport.setAttribute('viewBox', `${mapLeft} ${mapTop} ${mapWidth} ${mapHeight}`);
const bgImage = document.createElementNS('http://www.w3.org/2000/svg', 'image');
if (!openedSystem) {
if (!openedSystem && hasBackgroundImage($mapSettings)) {
bgImage.setAttribute('x', mapLeft.toString());
bgImage.setAttribute('y', mapTop.toString());
bgImage.setAttribute('width', mapWidth.toString());
Expand All @@ -212,16 +246,23 @@
}
const bgRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
bgRect.setAttribute('class', 'bg-rect');
bgRect.setAttribute('x', mapLeft.toString());
bgRect.setAttribute('y', mapTop.toString());
bgRect.setAttribute('width', mapWidth.toString());
bgRect.setAttribute('height', mapHeight.toString());
bgRect.setAttribute('fill', getBackgroundColor(colors, $mapSettings));
svgToExport.prepend(bgRect);
const legendContainer = document.createElementNS('http://www.w3.org/2000/svg', 'g');
legendContainer.setAttribute('transform', `translate(${mapLeft} ${mapTop}) scale(${scale})`);
legendContainer.innerHTML = openedSystem ? '' : legendTarget.innerHTML;
svgToExport.append(legendContainer);
const svgString = svgToExport.outerHTML;
if (!openedSystem) svgToExport.removeChild(bgImage);
if (!openedSystem && hasBackgroundImage($mapSettings)) svgToExport.removeChild(bgImage);
svgToExport.removeChild(bgRect);
svgToExport.removeChild(legendContainer);
const savePath = await stellarMapsApi.dialog.save({
defaultPath: await stellarMapsApi.path
Expand Down Expand Up @@ -445,6 +486,9 @@
fill={`rgba(${$mapSettings.terraIncognitaBrightness},${$mapSettings.terraIncognitaBrightness},${$mapSettings.terraIncognitaBrightness})`}
/>
{/if}
<g transform="translate({mapLeft} {mapTop}) scale({scale})">
<Legend data={mapData} {colors} />
</g>
{/if}
<path
fill="rgba(150, 150, 150, 0.5)"
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export const VERSION = '0.12.0-dev';

export const SYSTEM_FONTS =
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
32 changes: 26 additions & 6 deletions src/renderer/src/lib/convertSvgToPng.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface ConvertSvgToPngOptions {
outputWidth: number;
outputHeight: number;
backgroundImageUrl?: string;
foregroundImageUrl?: string;
backgroundColor?: string;
}
export default async function convertSvgToPng(
Expand All @@ -19,8 +20,9 @@ export default async function convertSvgToPng(
height,
outputWidth,
outputHeight,
backgroundImageUrl,
backgroundColor,
backgroundImageUrl,
foregroundImageUrl,
}: ConvertSvgToPngOptions,
) {
const canvas = document.createElement('canvas');
Expand All @@ -44,6 +46,19 @@ export default async function convertSvgToPng(
});
bgImg.src = backgroundImageUrl ?? '';

const fgImg = document.createElement('img');
fgImg.width = outputWidth;
fgImg.height = outputHeight;
fgImg.style.display = 'none';
document.body.append(fgImg);
const fgImgReady =
foregroundImageUrl == null
? Promise.resolve()
: new Promise<void>((resolve) => {
fgImg.addEventListener('load', () => resolve(), { once: true });
});
fgImg.src = foregroundImageUrl ?? '';

const img = document.createElement('img');
img.width = outputWidth;
img.height = outputHeight;
Expand All @@ -65,6 +80,10 @@ export default async function convertSvgToPng(
ctx.drawImage(bgImg, 0, 0);
}
ctx.drawImage(img, 0, 0);
if (foregroundImageUrl != null) {
await fgImgReady;
ctx.drawImage(fgImg, 0, 0);
}
canvas.toBlob(
async (b) => {
if (b == null) {
Expand All @@ -78,20 +97,21 @@ export default async function convertSvgToPng(
);
document.body.removeChild(img);
document.body.removeChild(bgImg);
document.body.removeChild(fgImg);
canvas.remove();
},
{ once: true },
);
});

const bgRect = svg.firstChild as SVGRectElement;
const bgRect = svg.querySelector('.bg-rect') as SVGRectElement | null;
svg.setAttribute('width', outputWidth.toString());
svg.setAttribute('height', outputHeight.toString());
svg.setAttribute('viewBox', `${left} ${top} ${width} ${height}`);
bgRect.setAttribute('x', left.toString());
bgRect.setAttribute('y', top.toString());
bgRect.setAttribute('width', width.toString());
bgRect.setAttribute('height', height.toString());
bgRect?.setAttribute('x', left.toString());
bgRect?.setAttribute('y', top.toString());
bgRect?.setAttribute('width', width.toString());
bgRect?.setAttribute('height', height.toString());
const svgUrl = URL.createObjectURL(new Blob([svg.outerHTML], { type: 'image/svg+xml' }));

img.src = svgUrl;
Expand Down
10 changes: 9 additions & 1 deletion src/renderer/src/lib/map/Legend.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import { SYSTEM_FONTS } from '../constants';
import { mapSettings } from '../settings';
import type { MapData } from './data/processMapData';
import Icons from './Icons.svelte';
Expand Down Expand Up @@ -29,7 +30,13 @@
</script>

{#if data?.legend.items.length && $mapSettings.legend}
<svg viewBox="0 0 {width} {height}" width="{width}px" height="{height}px">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 {width} {height}"
width="{width}px"
height="{height}px"
>
<defs>
{#if colors}
<OccupationPatternDefs {colors} {data} />
Expand Down Expand Up @@ -117,6 +124,7 @@
y={symbolSize / 2}
dominant-baseline="central"
font-size={$mapSettings.legendFontSize}
font-family={SYSTEM_FONTS}
>
{item.label}
</text>
Expand Down

0 comments on commit 5efb1c7

Please sign in to comment.