Skip to content

Commit

Permalink
feat-177: editor formatter and calculate volume
Browse files Browse the repository at this point in the history
  • Loading branch information
André Canuto authored and André Canuto committed Oct 27, 2023
1 parent eac35f7 commit 63f95db
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 160 deletions.
120 changes: 120 additions & 0 deletions src/editor/formatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { IaraInference } from "../speech";

export class IaraEditorInferenceFormatter {
constructor() {}

public _addTrailingSpaces(
text: string,
wordAfter: string,
wordBefore: string
) {
const addSpaceBefore =
wordBefore.length &&
!wordBefore.endsWith(" ") &&
!/^[\.,:;?!]/.test(text);

const addSpaceAfter =
wordAfter.length &&
!wordAfter.startsWith(" ") &&
!/^[\.,:;?!]/.test(wordAfter);

return `${addSpaceBefore ? " " : ""}${text}${addSpaceAfter ? " " : ""}`;
}

public _capitalize(text: string, wordBefore: string) {
const capitalize = !wordBefore.length || /[\.:;?!]$/.test(wordBefore);
return capitalize
? `${text.charAt(0).toLocaleUpperCase()}${text.slice(1)}`
: text;
}

protected _estimateVolume(text: string, regex: string) {
let converted = false;
const iterator = text.matchAll(RegExp(regex, 'giu'));
const matches = [...iterator];

matches.forEach((match) => {
// Check if all desired groups were captured
if (match && match.length === 7) {
// Volume estimation given 3 elipsoid radius
// original formula is: 4/3 * π * a * b * c
// where a, b and c are elipsoid radius
let volume =
(4 / 3) *
Math.PI *
parseFloat(match[1].replace(',', '.')) *
parseFloat(match[3].replace(',', '.')) *
parseFloat(match[5].replace(',', '.'));

// If user dictated measures as diameter
// then each one must be converted to radius:
// (4/3 * π * (a/2) * (b/2) * (c/2))

// Volume estimation given 3 elipsoid radius
volume /= 8;

if (volume >= 1000 && match[6] == 'mm') {
// convert the volume from mm³ to cm³
volume /= 1000;
converted = true;
}
// Round volume to 2 decimal places
const estimation = `${
Math.round(volume * Math.pow(10, 2)) / Math.pow(10, 2)
}`.replace('.', ',');

text = text.replace(
match[0],
`${match[0].replace('por', 'x')} (volume estimado em ${estimation} ${converted ? 'cm' : match[6]}${match[6] === 'cm'||match[6] === 'mm' ? '³': ''})`,
);
}
});
return text;
}

protected _parseMeasurements(text: string): string {
const numberMap = [
{ um: '1' },
{ dois: '2' },
{ três: '3' },
{ quatro: '4' },
{ cinco: '5' },
{ seis: '6' },
{ sete: '7' },
{ oito: '8' },
{ nove: '9' }
]
//convert the number by extensive number before the 'por' into numerals and change 'por' to 'x'
text = numberMap.reduce((a, c) => {
const [[oldText, newText]] = Object.entries(c)
return a.replace(new RegExp(`${oldText} (por|x)`, 'gui'), `${newText} x`)
}, text)
//convert the number by extensive after the 'por' into numerals and change 'por' to 'x'
text = numberMap.reduce((a, c) => {
const [[oldText, newText]] = Object.entries(c)
return a.replace(new RegExp(`(por|x) ${oldText}`, 'gui'), `x ${newText}`)
}, text)

//convert the 'por' before or after a number and return the formatted expression without a space ex:1x1
text = text.replace(/(\d+(?:,\d+)?) (por|x) (?=\d+(?:,\d+)?)/gui, '$1x')

return text
}

format(inference: IaraInference, _wordBefore: string, _wordAfter: string) {
let text = inference.richTranscript
.replace(/^<div>/, "")
.replace(/<\/div>$/, "");

text = this._parseMeasurements(text);

// expression to estimate volume
text = this._estimateVolume(text, '(\\d+(?:,\\d+)?)(\\spor\\s|x)(\\d+(?:,\\d+)?)(\\spor\\s|x)(\\d+(?:,\\d+)?) (cm³|mm³)(?!\\s\\()');
text = this._estimateVolume(text, '(\\d+(?:,\\d+)?)(\\spor\\s|x)(\\d+(?:,\\d+)?)(\\spor\\s|x)(\\d+(?:,\\d+)?) (cm|mm)(?!\\s\\(|³)');

text = this._addTrailingSpaces(text, _wordAfter, _wordBefore);
text = this._capitalize(text, _wordBefore);

return text;
}
}
83 changes: 0 additions & 83 deletions src/editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,87 +73,4 @@ export abstract class EditorAdapter {
): Promise<void> {
return this._recognition.report.change(plainContent, richContent);
}

protected _estimateVolume(text: string, regex: string) {
let converted = false;
const iterator = text.matchAll(RegExp(regex, 'giu'));
const matches = [...iterator];

matches.forEach((match) => {
// Check if all desired groups were captured
if (match && match.length === 7) {
// Volume estimation given 3 elipsoid radius
// original formula is: 4/3 * π * a * b * c
// where a, b and c are elipsoid radius
let volume =
(4 / 3) *
Math.PI *
parseFloat(match[1].replace(',', '.')) *
parseFloat(match[3].replace(',', '.')) *
parseFloat(match[5].replace(',', '.'));

// If user dictated measures as diameter
// then each one must be converted to radius:
// (4/3 * π * (a/2) * (b/2) * (c/2))

// Volume estimation given 3 elipsoid radius
volume /= 8;

if (volume >= 1000 && match[6] == 'mm') {
// convert the volume from mm³ to cm³
volume /= 1000;
converted = true;
}
// Round volume to 2 decimal places
const estimation = `${
Math.round(volume * Math.pow(10, 2)) / Math.pow(10, 2)
}`.replace('.', ',');

text = text.replace(
match[0],
`${match[0].replace('por', 'x')} (volume estimado em ${estimation} ${converted ? 'cm' : match[6]}${match[6] === 'cm'||match[6] === 'mm' ? '³': ''})`,
);
}
});
return text;
}

protected _parseMeasurements(text: string): string {
const numberMap = [
{ um: '1' },
{ dois: '2' },
{ três: '3' },
{ quatro: '4' },
{ cinco: '5' },
{ seis: '6' },
{ sete: '7' },
{ oito: '8' },
{ nove: '9' }
]
//convert the number by extensive number before the 'por' into numerals and change 'por' to 'x'
text = numberMap.reduce((a, c) => {
const [[oldText, newText]] = Object.entries(c)
return a.replace(new RegExp(`${oldText} (por|x)`, 'gui'), `${newText} x`)
}, text)
//convert the number by extensive after the 'por' into numerals and change 'por' to 'x'
text = numberMap.reduce((a, c) => {
const [[oldText, newText]] = Object.entries(c)
return a.replace(new RegExp(`(por|x) ${oldText}`, 'gui'), `x ${newText}`)
}, text)

//convert the 'por' before or after a number and return the formatted expression without a space ex:1x1
text = text.replace(/(\d+(?:,\d+)?) (por|x) (?=\d+(?:,\d+)?)/gui, '$1x')

return text
}

inferenceFormatter(text: string): string {
let formatted = this._parseMeasurements(text);

// expression to estimate volume
formatted = this._estimateVolume(formatted, '(\\d+(?:,\\d+)?)(\\spor\\s|x)(\\d+(?:,\\d+)?)(\\spor\\s|x)(\\d+(?:,\\d+)?) (cm³|mm³)(?!\\s\\()')
formatted = this._estimateVolume(formatted, '(\\d+(?:,\\d+)?)(\\spor\\s|x)(\\d+(?:,\\d+)?)(\\spor\\s|x)(\\d+(?:,\\d+)?) (cm|mm)(?!\\s\\(|³)')

return formatted;
}
}
70 changes: 0 additions & 70 deletions src/syncfusion/formatter.ts

This file was deleted.

41 changes: 34 additions & 7 deletions src/syncfusion/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import type {
} from "@syncfusion/ej2-documenteditor";
import { EditorAdapter } from "../editor";
import { IaraInference } from "../speech";
import { IaraSyncfusionInferenceFormatter } from "./formatter";
import { IaraEditorInferenceFormatter } from "../editor/formatter";

interface SelectionOffsets {
end: string;
start: string;
}

export class IaraSyncfusionAdapter
extends EditorAdapter
Expand All @@ -14,7 +19,7 @@ export class IaraSyncfusionAdapter
private _initialUndoStackSize = 0;
public savingReportSpan = document.createElement("span");
public timeoutToSave: any;
private _syncfusionFormatter: IaraSyncfusionInferenceFormatter;
private _editorFormatter: IaraEditorInferenceFormatter;

private get _editorAPI(): Editor {
return this._editor.editor;
Expand All @@ -30,10 +35,7 @@ export class IaraSyncfusionAdapter
super(_editor, _recognition);
this._editor.contentChange = this._onContentChange.bind(this);
this._editor.enableLocalPaste = true;
this._syncfusionFormatter = new IaraSyncfusionInferenceFormatter(
this._editorSelection,
this
);
this._editorFormatter = new IaraEditorInferenceFormatter();
}

getUndoStackSize(): number {
Expand Down Expand Up @@ -91,6 +93,20 @@ export class IaraSyncfusionAdapter
return content;
}

private _getWordAfterSelection(selectionOffsets: SelectionOffsets): string {
this._editorSelection.extendToWordEnd();
const wordAfter = this._editorSelection.text.trimEnd();
this._editorSelection.select(selectionOffsets.start, selectionOffsets.end);
return wordAfter;
}

private _getWordBeforeSelection(selectionOffsets: SelectionOffsets): string {
this._editorSelection.extendToWordStart();
const wordBefore = this._editorSelection.text.trimStart();
this._editorSelection.select(selectionOffsets.start, selectionOffsets.end);
return wordBefore;
}

insertInference(inference: IaraInference) {
if (inference.isFirst) {
if (this._editorSelection.text.length) this._editorAPI.delete();
Expand All @@ -102,7 +118,18 @@ export class IaraSyncfusionAdapter
}

// Syncfusion formatter
let text = this._syncfusionFormatter.format(inference);
let text = inference.richTranscript
.replace(/^<div>/, "")
.replace(/<\/div>$/, "");

const initialSelectionOffsets = {
end: this._editorSelection.endOffset,
start: this._editorSelection.startOffset,
};
const wordBefore = this._getWordBeforeSelection(initialSelectionOffsets);
const wordAfter = this._getWordAfterSelection(initialSelectionOffsets);

text = this._editorFormatter.format(inference, wordBefore, wordAfter);

const [firstLine, ...lines]: string[] = text.split("</div><div>");
this.insertText(firstLine);
Expand Down

0 comments on commit 63f95db

Please sign in to comment.