Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement basic vertical alignment #103

Merged
merged 1 commit into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions app/components/settings-form/settings.hbs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<fieldset class="uk-fieldset">

<label class="uk-form-label" for="text-content">
{{t "text_settings.type"}}
</label>
<Ui::TypeSelect
<Ui::TypeSelect
@value={{@model.type}}
@onChange={{this.changeType}}
data-test-settings-type
Expand All @@ -21,7 +21,7 @@
@debounce={{250}}
data-test-settings-text
/>
{{else}}
{{else}}
<Ui::Input
id="text-content"
class="uk-input"
Expand Down Expand Up @@ -52,6 +52,22 @@
</div>
{{/if}}

<div class="uk-margin">
<label class="uk-form-label" for="text-alignment">
{{t "text_settings.valignment"}}
</label>
<Ui::RadioGroup
@radioName="text-alignment"
@options={{this.vAlignmentOptions}}
@checked={{@model.vAlignment}}
@onChange={{fn (mut @model.vAlignment)}}
class="uk-grid-small uk-child-width-auto uk-grid"
data-test-settings-text-valignment
as |align|>
{{t (concat "valignment." align)}}
</Ui::RadioGroup>
</div>

<div class="uk-margin">
<label class="uk-form-label" for="text-size">
{{t "text_settings.size"}}
Expand All @@ -71,7 +87,7 @@
</div>

<div class="uk-margin">

<label class="uk-form-label" for="text-height">
{{t "text_settings.height"}}
</label>
Expand Down
6 changes: 6 additions & 0 deletions app/components/settings-form/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default class SettingsFormTextSettings extends Component<SettingsFormText
}

alignmentOptions = ['left', 'center', 'right'];
vAlignmentOptions = ['default', 'top', 'bottom'];

@action
setInt(props: 'size' | 'height' | 'spacing' | 'vSpacing', value: string) {
Expand All @@ -47,5 +48,10 @@ export default class SettingsFormTextSettings extends Component<SettingsFormText
if (!this.enableMultiline) {
this.args.model.text = this.args.model.text.split('\n').join(' ');
}

// Force switch to bottom align when type is changed to vertical text
if (type === ModelType.VerticalTextWithSupport) {
this.args.model.vAlignment = 'bottom';
}
}
}
1 change: 1 addition & 0 deletions app/config/environment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ declare const config: {
spacing: number;
vSpacing: number;
alignment: 'left' | 'center' | 'right';
vAlignment: 'default' | 'top' | 'bottom';
type: ModelType;
supportHeight: number;
supportBorderRadius: number;
Expand Down
5 changes: 5 additions & 0 deletions app/models/text-maker-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Handle,
ModelType,
TextMakerAlignment,
TextMakerVerticalAlignment,
} from 'text2stl/services/text-maker';
import { tracked } from '@glimmer/tracking';
import config from 'text2stl/config/environment';
Expand Down Expand Up @@ -104,6 +105,7 @@ export default class TextMakerSettings implements TextMakerParameters, QPSeriali
@tracked spacing?: number;
@tracked vSpacing?: number;
@tracked alignment: TextMakerAlignment;
@tracked vAlignment: TextMakerVerticalAlignment;
@tracked type: ModelType;
@tracked supportHeight?: number;
@tracked supportBorderRadius?: number;
Expand All @@ -119,6 +121,7 @@ export default class TextMakerSettings implements TextMakerParameters, QPSeriali
this.spacing = args.spacing ?? textMakerDefault.spacing;
this.vSpacing = args.vSpacing ?? textMakerDefault.vSpacing;
this.alignment = args.alignment ?? textMakerDefault.alignment;
this.vAlignment = args.vAlignment ?? textMakerDefault.vAlignment;
this.type = args.type ?? textMakerDefault.type;
this.supportHeight = args.supportHeight ?? textMakerDefault.supportHeight;
this.supportBorderRadius = args.supportBorderRadius ?? textMakerDefault.supportBorderRadius;
Expand All @@ -141,6 +144,7 @@ export default class TextMakerSettings implements TextMakerParameters, QPSeriali
spacing: this.spacing,
vSpacing: this.vSpacing,
alignment: this.alignment,
vAlignment: this.vAlignment,
type: this.type,
supportHeight: this.supportHeight,
supportBorderRadius: this.supportBorderRadius,
Expand All @@ -164,6 +168,7 @@ export default class TextMakerSettings implements TextMakerParameters, QPSeriali
this.spacing = v.spacing;
this.vSpacing = v.vSpacing;
this.alignment = v.alignment;
this.vAlignment = v.vAlignment;
this.type = v.type;
this.supportHeight = v.supportHeight;
this.supportBorderRadius = v.supportBorderRadius;
Expand Down
48 changes: 46 additions & 2 deletions app/services/text-maker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
} = config;

export type TextMakerAlignment = 'left' | 'center' | 'right';
export type TextMakerVerticalAlignment = 'default' | 'top' | 'bottom';

export type SupportPadding = {
top: number;
Expand All @@ -37,6 +38,7 @@ export interface TextMakerParameters {
spacing?: number;
vSpacing?: number;
alignment?: TextMakerAlignment;
vAlignment?: TextMakerVerticalAlignment;
type?: ModelType;
supportHeight?: number;
supportPadding?: SupportPadding;
Expand Down Expand Up @@ -209,12 +211,16 @@ export default class TextMakerService extends Service {
const vSpacing = params.vSpacing !== undefined ? params.vSpacing : textMakerDefault.vSpacing;
const alignment =
params.alignment !== undefined ? params.alignment : textMakerDefault.alignment;
const vAlignment =
params.vAlignment !== undefined ? params.vAlignment : textMakerDefault.vAlignment;

const scale = (1 / font.unitsPerEm) * size;

const glyphShapes: SingleGlyphDef[] = [];
// to handle alignment
const linesWidth: number[] = [];
const linesMaxY: { maxY: number; minY: number }[] = [];
const linesGlyphInfos: Array<Array<{ height: number; maxY: number; minY: number }>> = [];
const bounds = {
min: { x: Number.MAX_SAFE_INTEGER, y: Number.MAX_SAFE_INTEGER },
max: { x: 0, y: 0 },
Expand All @@ -227,12 +233,28 @@ export default class TextMakerService extends Service {
for (const lineText of lines) {
let dx = 0;
let lineMaxX = 0;
const lineMaxY = { minY: Number.MAX_SAFE_INTEGER, maxY: -Number.MAX_SAFE_INTEGER };
const lineGlyphInfos: { height: number; maxY: number; minY: number }[] = [];

font.forEachGlyph(lineText, 0, 0, size, undefined, (glyph, x, y) => {
x += dx;
dx += spacing;
const glyphBounds = glyph.getBoundingBox();

lineMaxX = x + glyphBounds.x2 * scale;
const glyphHeight = (glyphBounds.y2 - glyphBounds.y1) * scale;

const minY = Math.min(glyphBounds.y1, glyphBounds.y2) * scale;
const maxY = Math.max(glyphBounds.y1, glyphBounds.y2) * scale;

lineMaxY.maxY = Math.max(lineMaxY.maxY, maxY);
lineMaxY.minY = Math.min(lineMaxY.minY, minY);

lineGlyphInfos.push({
height: glyphHeight,
maxY,
minY,
});

bounds.min.x = Math.min(bounds.min.x, x + glyphBounds.x1 * scale);
bounds.min.y = Math.min(bounds.min.y, y - dy + glyphBounds.y1 * scale);
Expand All @@ -244,11 +266,13 @@ export default class TextMakerService extends Service {

// Keep this for each line to handle alignment
linesWidth.push(lineMaxX);
linesMaxY.push(lineMaxY);
linesGlyphInfos.push(lineGlyphInfos);
}

const linesAlignOffset = linesWidth.map(() => 0);

// Handle alignment (now we know all line size)
// Handle horizontal alignment (now we know all line size)
if (alignment !== 'left') {
const maxWidth = Math.max(...linesWidth);

Expand All @@ -264,11 +288,23 @@ export default class TextMakerService extends Service {
for (const lineIndex in lines) {
const lineText = lines[lineIndex];
let dx = 0;
let glyphIndex = 0;

// Iterate on text char to generate a Geometry for each
font.forEachGlyph(lineText, 0, 0, size, undefined, (glyph, x, y) => {
x += dx + linesAlignOffset[lineIndex];

if (vAlignment !== 'default') {
const lineMaxY = linesMaxY[lineIndex];
const glyphInfo = linesGlyphInfos[lineIndex][glyphIndex];

if (vAlignment === 'bottom' && lineMaxY.minY !== glyphInfo.minY) {
y += lineMaxY.minY - glyphInfo.minY;
} else if (vAlignment === 'top' && lineMaxY.maxY !== glyphInfo.maxY) {
y += lineMaxY.maxY - glyphInfo.maxY;
}
}

glyphShapes.push(
this.glyphToShapes(
font.outlinesFormat,
Expand All @@ -279,6 +315,7 @@ export default class TextMakerService extends Service {
),
);
dx += spacing;
glyphIndex++;
});

dy += size + vSpacing;
Expand Down Expand Up @@ -313,6 +350,9 @@ export default class TextMakerService extends Service {
const textDepth =
params.height !== undefined && params.height >= 0 ? params.height : textMakerDefault.height;

const vAlignment =
params.vAlignment !== undefined ? params.vAlignment : textMakerDefault.vAlignment;

const plyghsDef = this.stringToGlyhpsDef(params, font);
let supportShape: THREE.Shape | undefined = undefined;

Expand Down Expand Up @@ -445,13 +485,17 @@ export default class TextMakerService extends Service {
),
);
} else if (type === ModelType.VerticalTextWithSupport) {
// Ensure bottom of the text is touching the support
const verticalOffset = vAlignment === 'bottom' ? Math.min(0, plyghsDef.bounds.min.y) : 0;

// Rotate & move text
textGeometry.applyMatrix4(new THREE.Matrix4().makeRotationX(Math.PI / 2));
textGeometry.applyMatrix4(
new THREE.Matrix4().makeTranslation(
supportPadding.left,
supportPadding.bottom + size.z * 2,
supportDepth,

supportDepth - verticalOffset,
),
);
}
Expand Down
1 change: 1 addition & 0 deletions config/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = function (environment) {
spacing: 2,
vSpacing: 0,
alignment: 'center',
vAlignment: 'default',
type: 2,
supportHeight: 5,
supportBorderRadius: 5,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ module('Integration | Component | settings-form/settings', function (hooks) {
await fillIn('[data-test-settings-spacing]', '789');
assert.strictEqual(model.spacing, 789, 'model.spacing was updated');

assert
.dom(`[data-test-settings-text-valignment] input[type="radio"][value="${model.vAlignment}"]`)
.isChecked('current textAlignment radio is checked');
await click('[data-test-settings-text-valignment] input[type="radio"][value="bottom"]');
assert.strictEqual(model.vAlignment, 'bottom', 'model.vAlignment was updated');

assert
.dom('[data-test-settings-supportHeight]')
.doesNotExist('supportHeight field is not displayed when model.type is text only');
Expand Down Expand Up @@ -108,7 +114,7 @@ module('Integration | Component | settings-form/settings', function (hooks) {
assert.dom('[data-test-settings-text-alignment]').exists();
assert
.dom(`[data-test-settings-text-alignment] input[type="radio"][value="${model.alignment}"]`)
.hasValue('center', 'current textAlignment radio is checked');
.isChecked('current textAlignment radio is checked');
await click('[data-test-settings-text-alignment] input[type="radio"][value="right"]');
assert.strictEqual(model.alignment, 'right', 'model.alignment was updated');

Expand Down
5 changes: 5 additions & 0 deletions translations/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ text_settings:
supportHeight: Support height
supportPadding: Support spacing
alignment: Text alignment
valignment: Text vertical alignement
supportBorderRadius: Border radius
handleSettings:
label: Handle settings
Expand All @@ -75,6 +76,10 @@ alignment:
left: Left
center: Center
right: Right
valignment:
default: Default
top: Top
bottom: Bottom
directions:
top: Top
bottom: Bottom
Expand Down
5 changes: 5 additions & 0 deletions translations/fr-fr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ text_settings:
supportHeight: Hauteur du support
supportPadding: Espacement du support
alignment: Alignement du texte
valignment: Alignement vertical du texte
supportBorderRadius: Rayon des coins
handleSettings:
label: Option d'accroche
Expand All @@ -72,6 +73,10 @@ alignment:
left: Gauche
center: Centré
right: Droite
valignment:
default: Auto
top: Haut
bottom: Bas
directions:
top: Haut
bottom: Bas
Expand Down
Loading