microsoft / maker.js

📐⚙ 2D vector line drawing and shape modeling for CNC and laser cutters.
http://maker.js.org
Apache License 2.0
1.78k stars 275 forks source link

time to compute combineUnion #477

Open akshaynikhare opened 3 years ago

akshaynikhare commented 3 years ago

we are building a too for a Vinyl printing shop and we need to optmize a lot of text in a model. few of our font have complicated geometry like below , and the text overlap, we are using "combineUnionfor" for every letter in the text , but its taking too long to get an output , image

so the Question is does combineUnion take this long to compute the path (very long about 20-30 seconds for the below code which can be tested in playgraound )

here is my code

var makerjs = require('makerjs');
var computeLayout = require('opentype-layout');
function DraftText(font, text, fontSize) `{`
        var textModel = { models: {} };
        var scale = 1 / font.unitsPerEm * fontSize;
        var layoutOptions = {
            lineHeight: 1 * font.unitsPerEm,
            width: 500 / scale
        };
        var layout = computeLayout(font, text, layoutOptions);
        layout.glyphs.forEach((glyph,i) => {
            var character = makerjs.models.Text.glyphToModel(glyph.data, fontSize);
            character.origin = makerjs.point.scale(glyph.position, scale);
            makerjs.model.simplify(character, { pointMatchingDistance: 0.001 });
            makerjs.model.combineUnion(textModel, character);
            makerjs.model.addModel(textModel, character, i);
         });
        return textModel;
    }
function Nameplate(font,font_scale, text,icon ) {
   var textModel = DraftText(font, text, font_scale);
   this.models = {
        text: textModel
   };
   makerjs.model.zero(this);
   this.units = makerjs.unitType.Millimeter;
}
Nameplate.metaParameters = [
    { title: "font", type: "font", value: '*' },
    { title: "font size", type: "range", min: 10, max: 200, value:  20},
    { title: "text", type: "text", value: 'Hello World! This box should start word-wrapping!' }
];
module.exports = Nameplate;

am i doing something wrong?

danmarshall commented 3 years ago

Hello, no it's nothing you are doing wrong. Currently it is a brute force naïve O-squared implementation. I have been working on a solution to this for a while, but I do not have release-ready results yet. If you're brave enough to try, see the https://github.com/microsoft/maker.js/tree/combine-sweep-expand-wip branch.

The workaround you can do now is to cache as much as possible. Perhaps offline you can run kerning pairs of characters to see which overlap and which do not, and grab pre-computed characters instead of a "live" union. Hopefully that makes sense.

akshaynikhare commented 3 years ago

Can we not make the complete process GPU based it will increase the time to process.

On Fri, Dec 4, 2020, 06:31 Dan Marshall notifications@github.com wrote:

Hello, no it's nothing you are doing wrong. Currently it is a brute force naïve O-squared implementation. I have been working on a solution to this for a while, but I do not have release-ready results yet. If you're brave enough to try, see the https://github.com/microsoft/maker.js/tree/combine-sweep-expand-wip branch.

The workaround you can do now is to cache as much as possible. Perhaps offline you can run kerning pairs of characters to see which overlap and which do not, and grab pre-computed characters instead of a "live" union. Hopefully that makes sense.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/microsoft/maker.js/issues/477#issuecomment-738481838, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACKGLGD7FFKT57QAM6CN2OTSTAYH7ANCNFSM4UMSCRZQ .

atimmer commented 3 years ago

Sharing this in case someone else runs into this. When you don't know if the font requires combining, the combineUnion calculations are wasted most of the time. In most fonts, letters are very well separated. This can be improved by first comparing the 'bounding box' of the letters before combining them into one model. Like this:

for ( let i = 1; i < size; i++ ) {
    let extend1 = makerjs.measure.modelExtents( { models: combinedModels } );
    let extend2 = makerjs.measure.modelExtents( models[ i ] );

    // When the model extension is not an object, assume it is a space.
    // It also assumes that the first character is not a space.
    // So before this function, the text should have been trimmed.
    if ( ! isObject( extend2 ) ) {
        continue;
    }

    // Combining is very slow, so first check if the measurements are overlapping.
    // If they don't overlap, we are guaranteed that we don't have to combine.
    if ( makerjs.measure.isMeasurementOverlapping( extend1, extend2 ) ) {
        makerjs.model.combineUnion( { models: combinedModels }, models[i] )
    }

    combinedModels = {
        ...combinedModels,
        [ i ]: models[ i ],
    }
}

This cool trick also exists in the some of the code of Maker.JS 😁

akshaynikhare commented 3 years ago

@atimmer this might work for some of the font, which is actually good , i will try this in my code 🤞