Skip to content

Commit

Permalink
feat(122): Add option to choose export format (STL or OBJ) (#127)
Browse files Browse the repository at this point in the history
* feat(122): Add option to choose export format (STL or OBJ)

* try fix flaky  test
  • Loading branch information
romgere authored Mar 7, 2024
1 parent f055cd5 commit f71b779
Show file tree
Hide file tree
Showing 12 changed files with 423 additions and 131 deletions.
3 changes: 3 additions & 0 deletions app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import '@esri/calcite-components/dist/components/calcite-combobox';
import '@esri/calcite-components/dist/components/calcite-combobox-item';
import '@esri/calcite-components/dist/components/calcite-card';
import '@esri/calcite-components/dist/components/calcite-popover';
import '@esri/calcite-components/dist/components/calcite-split-button';
import '@esri/calcite-components/dist/components/calcite-dropdown-group';
import '@esri/calcite-components/dist/components/calcite-dropdown-item';

export default class App extends Application {
modulePrefix = config.modulePrefix;
Expand Down
28 changes: 21 additions & 7 deletions app/controllers/app/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import FontManagerService from 'text2stl/services/font-manager';
import TextMakerService from 'text2stl/services/text-maker';
import STLExporterService from 'text2stl/services/stl-exporter';
import FileExporterService from 'text2stl/services/file-exporter';
import { tracked } from '@glimmer/tracking';
import { trackedFunction } from 'ember-resources/util/function';
import { Registry as Services } from '@ember/service';

import type ApplicationRoute from 'text2stl/routes/app/generator';
import type IntlService from 'ember-intl/services/intl';
import type { FileType } from 'text2stl/services/file-exporter';

export default class GeneratorController extends Controller {
queryParams = ['modelSettings'];
Expand All @@ -29,7 +30,7 @@ export default class GeneratorController extends Controller {

@service declare fontManager: FontManagerService;

@service declare stlExporter: STLExporterService;
@service declare fileExporter: FileExporterService;

@service declare intl: IntlService;

Expand Down Expand Up @@ -68,20 +69,33 @@ export default class GeneratorController extends Controller {

@tracked isFontLoading = true;

@tracked fileType: FileType = 'stl';

fileTypes: FileType[] = ['stl', 'obj'];

get exportFileLabel() {
return this.intl.t('export_file', { type: this.fileType.toUpperCase() });
}

@action
changeFileType(fileType: FileType) {
this.fileType = fileType;
}

@action
async exportSTL() {
async exportFile() {
const { value: mesh } = await this.mesh;

if (!mesh) {
return;
}

this._gtag('event', 'stl_download', {
event_category: 'stl', // eslint-disable-line camelcase
value: this.model.type,
this._gtag('event', 'file_download', {
event_category: 'file', // eslint-disable-line camelcase
value: this.fileType,
});

this.stlExporter.downloadMeshAsSTL(mesh);
this.fileExporter.downloadMeshFile(mesh, this.fileType);
}

@tracked saveModalVisible = false;
Expand Down
59 changes: 59 additions & 0 deletions app/services/file-exporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Service from '@ember/service';
import { STLExporter } from 'text2stl/utils/STLExporter';
import { OBJExporter } from 'text2stl/utils/OBJExporter';

import type { Mesh } from 'three';

export type FileType = 'stl' | 'obj';

export default class FileExporterService extends Service {
stlExporter = new STLExporter();
objExporter = new OBJExporter();

meshToSTLBlob(mesh: Mesh, binary: boolean): Blob {
const result = this.stlExporter.parse(mesh, { binary });
return new Blob([result], {
type: binary ? 'application/octet-stream' : 'text/plain',
});
}

meshToOBJBlob(mesh: Mesh): Blob {
const result = this.objExporter.parse(mesh);
return new Blob([result], {
type: 'text/plain',
});
}

downloadBlob(blob: Blob, name: string) {
const link = document.createElement('a');
link.style.display = 'none';
document.body.appendChild(link);

link.href = URL.createObjectURL(blob);
link.download = name;
link.click();
}

downloadMeshFile(mesh: Mesh, type: FileType = 'stl') {
let blob: Blob;

switch (type) {
case 'obj':
blob = this.meshToOBJBlob(mesh);
break;
default:
case 'stl':
blob = this.meshToSTLBlob(mesh, true);
break;
}

this.downloadBlob(blob, `output.${type}`);
}
}

// DO NOT DELETE: this is how TypeScript knows how to look up your services.
declare module '@ember/service' {
interface Registry {
'file-exporter': FileExporterService;
}
}
41 changes: 0 additions & 41 deletions app/services/stl-exporter.ts

This file was deleted.

80 changes: 47 additions & 33 deletions app/templates/app/generator.hbs
Original file line number Diff line number Diff line change
@@ -1,75 +1,89 @@
<calcite-shell-panel slot="panel-start" collapsed={{this.mainPanelClosed}}>
<calcite-action-bar slot="action-bar">

<calcite-shell-panel slot='panel-start' collapsed={{this.mainPanelClosed}}>
<calcite-action-bar slot='action-bar'>

<calcite-action-group>
<calcite-action
text={{t "settings_form.label"}}
icon="sliders"
{{on "click" (if this.mainPanelClosed this.openMainPanel this.closeMainPanel)}}
text={{t 'settings_form.label'}}
icon='sliders'
{{on 'click' (if this.mainPanelClosed this.openMainPanel this.closeMainPanel)}}
/>
</calcite-action-group>

<calcite-action-group>
<calcite-action
icon="save"
text={{t 'save'}}
{{on 'click' this.showSaveModal}}
/>
<calcite-action
icon="reset"
text={{t 'reset'}}
{{on 'click' this.showResetModal}}
/>
<calcite-action icon='save' text={{t 'save'}} {{on 'click' this.showSaveModal}} />
<calcite-action icon='reset' text={{t 'reset'}} {{on 'click' this.showResetModal}} />
</calcite-action-group>

<calcite-action-group slot="actions-end">
<calcite-action-group slot='actions-end'>
<calcite-action
icon="download"
text={{t 'export_stl'}}
icon='download'
text={{this.exportFileLabel}}
disabled={{this.exportDisabled}}
{{on 'click' this.exportSTL}}
{{on 'click' this.exportFile}}
/>
</calcite-action-group>

</calcite-action-bar>

<calcite-panel
heading={{t "settings_form.label"}}
heading={{t 'settings_form.label'}}
closable
closed={{this.mainPanelClosed}}
{{on "calcitePanelClose" this.closeMainPanel}}
{{on 'calcitePanelClose' this.closeMainPanel}}
>

<div slot="footer">
<calcite-button
width="full"
scale="l"
<div slot='footer'>
{{!-- <calcite-button
width='full'
scale='l'
disabled={{this.exportDisabled}}
loading={{this.meshGenerating}}
icon-start="download"
{{on 'click' this.exportSTL}}
icon-start='download'
{{on 'click' (fn this.exportFile 'stl')}}
data-test-export-stl
>
{{t 'export_stl'}}
</calcite-button>
</calcite-button> --}}

<calcite-split-button
primary-text={{this.exportFileLabel}}
width='full'
scale='l'
disabled={{this.exportDisabled}}
loading={{this.meshGenerating}}
primary-icon-start='download'
dropdown-icon-type='ellipsis'
data-test-export-stl
{{on 'calciteSplitButtonPrimaryClick' this.exportFile}}
>
<calcite-dropdown-group selection-mode='single'>
{{#each this.fileTypes as |type|}}
<calcite-dropdown-item
selected={{eq type this.fileType}}
{{on 'calciteDropdownItemSelect' (fn this.changeFileType type)}}
>
{{t 'export_file_type' type=type}}
</calcite-dropdown-item>
{{/each}}
</calcite-dropdown-group>
</calcite-split-button>

</div>

<SettingsForm @model={{this.model}} />
</calcite-panel>
</calcite-shell-panel>


<calcite-panel>
<ThreePreview @mesh={{this.mesh.value}} @parentSize={{true}} @nearCamera={{true}} as |preview|>
<ThreePreview @mesh={{this.mesh.value}} @parentSize={{true}} @nearCamera={{true}} as |preview|>

<div id="three-preview-container">
<div id='three-preview-container'>
<preview.renderer />
</div>
<div slot='footer'>
<preview.size />
</div>
</ThreePreview>
</ThreePreview>

</calcite-panel>

Expand Down
Loading

0 comments on commit f71b779

Please sign in to comment.