From 5b60f3d05ffcbb56a13cea006b59540bf7a4bea8 Mon Sep 17 00:00:00 2001 From: ffd8 Date: Sun, 22 Mar 2020 01:35:48 +0100 Subject: [PATCH 1/9] createOutlines() + pathToPoints() - added `createOutlines()` function, handles textFrames, textPaths, multi-line text, multi-box text. - added `pathToPoints()` function, handles polygons from `createOutlines()` or most other pageItems (oval, rect, polygon, etc), also accepts groups and arrays. - added tests for both functions - updated changelog - added nice error message for `textSize()`, common beginner mistake giving size less than zero. --- basil.js | 293 ++++++++++++++++++++++++++++++++++++- changelog.txt | 2 + src/includes/core.js | 2 +- src/includes/shape.js | 175 ++++++++++++++++++++++ src/includes/typography.js | 116 ++++++++++++++- test/shape-tests.jsx | 18 +++ test/typography-tests.jsx | 12 ++ 7 files changed, 614 insertions(+), 4 deletions(-) diff --git a/basil.js b/basil.js index 142509a8..87f89f20 100644 --- a/basil.js +++ b/basil.js @@ -819,7 +819,7 @@ var getParentFunctionName = function(level) { } var checkNull = function (obj) { - if(obj === null || typeof obj === undefined) error("Received null object."); + if(obj === null || typeof obj === undefined) error("Received null " + obj); }; var isEnum = function(base, value) { @@ -7728,6 +7728,159 @@ pub.vertex = function() { } }; +// ---------------------------------------- +// Shape/Path to Points +// ---------------------------------------- + +/** + * @summary Get points and bezier coordinates from path(s). + * @description Returns an object containing array of points, beziers, and both separated by paths. Intended for use with `createOutlines()` of text, but works with most pageItem paths. Accepts both single paths or a collection/group of paths. Both points and beziers will have connected lines (one big array), use paths followed by points or beziers for isolated shapes. + * + * @cat Shape + * @method pathToPoints + * @param {Object} obj The pageItem(s) to process point/bezier coordinates of + * @param {Number} [addPts] Optional amount of additional interpolated points. Ignore if using beziers. + * @return {Object} Returns object with following values: .points[], .beziers[], .paths[].points[], .paths[].beziers[] + * + * @example Points + * noFill(); + * var myEllipse = ellipse(width / 2, height / 2, width / 2, width / 2); + * var pts = pathToPoints(myEllipse); + * var ptsExtended = pathToPoints(myEllipse, 5); // add 5 points between points + * + * noStroke(); + * fill(0, 0, 255); + * + * // draw normal points + * for (var i=0; i < pts.points.length; i++) { + * var pt = pts.points[i]; + * ellipse(pt.x, pt.y, 10, 10); + * } + * + * noFill(); + * stroke(255, 0, 0); + * // draw extended points + * for (var i=0; i < ptsExtended.points.length; i++) { + * var pt = ptsExtended.points[i]; + * ellipse(pt.x, pt.y, 10, 10); + * } + * + * @example Beziers from text using createOutlines() + * textSize(400); + * var myText = text("H", width/4, height/4, 500, 500); + * var outlines = createOutlines(myText); + * var pts = pathToPoints(outlines); + * property(outlines, "fillColor", "None"); + * + * noFill(); + * stroke(0, 255, 0); + * + * // draw bezier + * beginShape(); + * for (var i=0; i < pts.beziers.length; i++) { + * var pt = pts.beziers[i]; // point w/ .anchor .left .right + * vertex(pt.anchor.x, pt.anchor.y, pt.left.x, pt.left.y, pt.right.x, pt.right.y); + * } + * endShape(CLOSE); + * + * // debug bezier + * for (var i=0; i < pts.beziers.length; i++) { + * var pt = pts.beziers[i]; // point + * var anchor = pt.anchor; // anchor + * var left = pt.left; // left handle + * var right = pt.right; // right handle + * noStroke(); + * fill(0); + * ellipse(anchor.x, anchor.y, 5, 5); + * ellipse(left.x, left.y, 5, 5); + * ellipse(right.x, right.y, 5, 5); + * stroke(0, 255, 0); + * line(anchor.x, anchor.y, left.x, left.y); + * line(anchor.x, anchor.y, right.x, right.y); + * } + * + * @example Isolated Paths of Beziers from text using createOutlines() + * textSize(200); + * var myText = text("hello", width/4, height/4, 500, 500); + * var outlines = createOutlines(myText); + * var pts = pathToPoints(outlines); + * property(outlines, "fillColor", "None"); + * + * noFill(); + * stroke(0, 255, 0); + * + * // just beziers + * for (var p=0; p < pts.paths.length; p++) { + * var path = pts.paths[p]; // each path isolated + * beginShape(); + * for (var i=0; i 0) { + currFontSize = pointSize; + }else{ + error("textSize() must be greater than 0."); + } } return currFontSize; }; @@ -8876,6 +9055,116 @@ pub.paragraphStyle = function(textOrName, props) { return style; }; +// ---------------------------------------- +// Typography/Outlines +// ---------------------------------------- + +/** + * @summary Convert text items to outlines. + * @description Returns a polygon or array of polygons (if text is multi-line, multi-boxed, or a textPath) and optionally processes them with a callback function. Intended for use with `pathToPoints()` for getting point and bezier coordinates from outlines. Warning, InDesign requires that text have a fill color in order to createOutlines. + * + * @cat Typography + * @method createOutlines + * @param {Story|TextFrame||Paragraph||Line||Word||Character||GraphicLine||Polygon||Oval||Rectangle} item Text or textpath to be outlined. + * @param {Function} [cb] Optional: The callback function to call with each polygon. Passed arguments: `obj`, `loopCounter` + * @return {Polygon | Array of Polygons} Returns a single or array of polygons depending on text converted. + * + * @example Points + * textSize(150); + * var myText = text("Hello", width/4, height/4, 500, 800); + * var outlines = createOutlines(myText); // create outlines + * var pts = pathToPoints(outlines); // get points + * // inspect(pts); // view structure + * for (var i=0; i < pts.points.length; i++) { + * var pt = pts.points[i]; // each point + * ellipse(pt.x, pt.y, 5, 5); + * } + * property(outlines, "fillColor", "None"); + * + * @example Points Extended + * textSize(150); + * var myText = text("Hello", width/4, height/4, 500, 800); + * var outlines = createOutlines(myText); // create outlines + * var pts = pathToPoints(outlines, 4); // get points, add 4 between each point + * for (var i=0; i < pts.points.length; i++) { + * var pt = pts.points[i]; // each point + * var s = map(i, 0, pts.points.length, .1, 5); + * ellipse(pt.x, pt.y, s, s); + * } + * property(outlines, "fillColor", "None"); + * + * @example Path isolated Beziers + * textSize(150); + * var myText = text("Hello", width/4, height/4, 500, 800); + * var outlines = createOutlines(myText); // create outlines + * var pts = pathToPoints(outlines); // get points + beziers + paths + * property(outlines, "fillColor", "None"); + * + * noFill(); + * stroke(0); + * + * // process paths -> beziers + * for (var p=0; p < pts.paths.length; p++) { + * var path = pts.paths[p]; // each path isolated + * beginShape(); + * for (var i=0; i < path.beziers.length; i++) { + * var tp = path.beziers[i]; + * var offset = sin(i * 5) * 15; // shift points + * vertex(tp.anchor.x + offset, tp.anchor.y, tp.left.x, tp.left.y, tp.right.x, tp.right.y); + * } + * endShape(CLOSE); + * } + * + */ + + +pub.createOutlines = function(item, cb) { + var outlines; + + // check textFrames, textPaths, or neither + if (item.hasOwnProperty('createOutlines')) { + outlines = item.createOutlines(); + } else if ( item instanceof GraphicLine || + item instanceof Oval || + item instanceof Rectangle || + item instanceof Polygon) { + if (item.textPaths.length > 0) { + outlines = item.textPaths[0].texts[0].createOutlines(); + } + } else { + error("Be sure to use:\n Story | TextFrame | Paragraph | Line | Word | Character | TextPath") + return false; + } + + // process outlines + if (outlines.length != undefined && outlines.length === 1) { + // multi-line text from group array + if (outlines[0] instanceof Group) { + outlines = ungroup(outlines[0]); + if (cb instanceof Function) { + return forEach(outlines, cb); + } else { + return outlines; + } + } + + // single polygon + if (cb instanceof Function) { + cb(outlines[0]); + } else { + return outlines[0]; + } + } else { + + // collection of polygons + if (cb instanceof Function) { + return forEach(outlines, cb); + } else { + return outlines; + } + } +} + // ---------------------------------------- // Typography/Constants // ---------------------------------------- diff --git a/changelog.txt b/changelog.txt index c154d99b..d4492d51 100644 --- a/changelog.txt +++ b/changelog.txt @@ -54,6 +54,8 @@ basil.js x.x.x DAY MONTH YEAR checks if a given variable is an integer + basil scripts don't try to start ExtendScript Toolkit in the background anymore + Added week() to return week number of the year ++ Added createOutlines() to convert text + textPaths to outlines paths ++ Added pathToPoints() to get points, beziers, and both isolated by paths, from pageItems * translate(), rotate() and scale() now behave like they do in Processing and change the current matrix * basil scripts will by default use the user's default units or the current units of the document, diff --git a/src/includes/core.js b/src/includes/core.js index 5a2cfa77..8ceff013 100644 --- a/src/includes/core.js +++ b/src/includes/core.js @@ -548,7 +548,7 @@ var getParentFunctionName = function(level) { } var checkNull = function (obj) { - if(obj === null || typeof obj === undefined) error("Received null object."); + if(obj === null || typeof obj === undefined) error("Received null " + obj); }; var isEnum = function(base, value) { diff --git a/src/includes/shape.js b/src/includes/shape.js index 046f89b6..91a625a7 100644 --- a/src/includes/shape.js +++ b/src/includes/shape.js @@ -547,6 +547,159 @@ pub.vertex = function() { } }; +// ---------------------------------------- +// Shape/Path to Points +// ---------------------------------------- + +/** + * @summary Get points and bezier coordinates from path(s). + * @description Returns an object containing array of points, beziers, and both separated by paths. Intended for use with `createOutlines()` of text, but works with most pageItem paths. Accepts both single paths or a collection/group of paths. Both points and beziers will have connected lines (one big array), use paths followed by points or beziers for isolated shapes. + * + * @cat Shape + * @method pathToPoints + * @param {Object} obj The pageItem(s) to process point/bezier coordinates of + * @param {Number} [addPts] Optional amount of additional interpolated points. Ignore if using beziers. + * @return {Object} Returns object with following values: .points[], .beziers[], .paths[].points[], .paths[].beziers[] + * + * @example Points + * noFill(); + * var myEllipse = ellipse(width / 2, height / 2, width / 2, width / 2); + * var pts = pathToPoints(myEllipse); + * var ptsExtended = pathToPoints(myEllipse, 5); // add 5 points between points + * + * noStroke(); + * fill(0, 0, 255); + * + * // draw normal points + * for (var i=0; i < pts.points.length; i++) { + * var pt = pts.points[i]; + * ellipse(pt.x, pt.y, 10, 10); + * } + * + * noFill(); + * stroke(255, 0, 0); + * // draw extended points + * for (var i=0; i < ptsExtended.points.length; i++) { + * var pt = ptsExtended.points[i]; + * ellipse(pt.x, pt.y, 10, 10); + * } + * + * @example Beziers from text using createOutlines() + * textSize(400); + * var myText = text("H", width/4, height/4, 500, 500); + * var outlines = createOutlines(myText); + * var pts = pathToPoints(outlines); + * property(outlines, "fillColor", "None"); + * + * noFill(); + * stroke(0, 255, 0); + * + * // draw bezier + * beginShape(); + * for (var i=0; i < pts.beziers.length; i++) { + * var pt = pts.beziers[i]; // point w/ .anchor .left .right + * vertex(pt.anchor.x, pt.anchor.y, pt.left.x, pt.left.y, pt.right.x, pt.right.y); + * } + * endShape(CLOSE); + * + * // debug bezier + * for (var i=0; i < pts.beziers.length; i++) { + * var pt = pts.beziers[i]; // point + * var anchor = pt.anchor; // anchor + * var left = pt.left; // left handle + * var right = pt.right; // right handle + * noStroke(); + * fill(0); + * ellipse(anchor.x, anchor.y, 5, 5); + * ellipse(left.x, left.y, 5, 5); + * ellipse(right.x, right.y, 5, 5); + * stroke(0, 255, 0); + * line(anchor.x, anchor.y, left.x, left.y); + * line(anchor.x, anchor.y, right.x, right.y); + * } + * + * @example Isolated Paths of Beziers from text using createOutlines() + * textSize(200); + * var myText = text("hello", width/4, height/4, 500, 500); + * var outlines = createOutlines(myText); + * var pts = pathToPoints(outlines); + * property(outlines, "fillColor", "None"); + * + * noFill(); + * stroke(0, 255, 0); + * + * // just beziers + * for (var p=0; p < pts.paths.length; p++) { + * var path = pts.paths[p]; // each path isolated + * beginShape(); + * for (var i=0; i 0) { + currFontSize = pointSize; + }else{ + error("textSize() must be greater than 0."); + } } return currFontSize; }; @@ -475,6 +479,116 @@ pub.paragraphStyle = function(textOrName, props) { return style; }; +// ---------------------------------------- +// Typography/Outlines +// ---------------------------------------- + +/** + * @summary Convert text items to outlines. + * @description Returns a polygon or array of polygons (if text is multi-line, multi-boxed, or a textPath) and optionally processes them with a callback function. Intended for use with `pathToPoints()` for getting point and bezier coordinates from outlines. Warning, InDesign requires that text have a fill color in order to createOutlines. + * + * @cat Typography + * @method createOutlines + * @param {Story|TextFrame||Paragraph||Line||Word||Character||GraphicLine||Polygon||Oval||Rectangle} item Text or textpath to be outlined. + * @param {Function} [cb] Optional: The callback function to call with each polygon. Passed arguments: `obj`, `loopCounter` + * @return {Polygon | Array of Polygons} Returns a single or array of polygons depending on text converted. + * + * @example Points + * textSize(150); + * var myText = text("Hello", width/4, height/4, 500, 800); + * var outlines = createOutlines(myText); // create outlines + * var pts = pathToPoints(outlines); // get points + * // inspect(pts); // view structure + * for (var i=0; i < pts.points.length; i++) { + * var pt = pts.points[i]; // each point + * ellipse(pt.x, pt.y, 5, 5); + * } + * property(outlines, "fillColor", "None"); + * + * @example Points Extended + * textSize(150); + * var myText = text("Hello", width/4, height/4, 500, 800); + * var outlines = createOutlines(myText); // create outlines + * var pts = pathToPoints(outlines, 4); // get points, add 4 between each point + * for (var i=0; i < pts.points.length; i++) { + * var pt = pts.points[i]; // each point + * var s = map(i, 0, pts.points.length, .1, 5); + * ellipse(pt.x, pt.y, s, s); + * } + * property(outlines, "fillColor", "None"); + * + * @example Path isolated Beziers + * textSize(150); + * var myText = text("Hello", width/4, height/4, 500, 800); + * var outlines = createOutlines(myText); // create outlines + * var pts = pathToPoints(outlines); // get points + beziers + paths + * property(outlines, "fillColor", "None"); + * + * noFill(); + * stroke(0); + * + * // process paths -> beziers + * for (var p=0; p < pts.paths.length; p++) { + * var path = pts.paths[p]; // each path isolated + * beginShape(); + * for (var i=0; i < path.beziers.length; i++) { + * var tp = path.beziers[i]; + * var offset = sin(i * 5) * 15; // shift points + * vertex(tp.anchor.x + offset, tp.anchor.y, tp.left.x, tp.left.y, tp.right.x, tp.right.y); + * } + * endShape(CLOSE); + * } + * + */ + + +pub.createOutlines = function(item, cb) { + var outlines; + + // check textFrames, textPaths, or neither + if (item.hasOwnProperty('createOutlines')) { + outlines = item.createOutlines(); + } else if ( item instanceof GraphicLine || + item instanceof Oval || + item instanceof Rectangle || + item instanceof Polygon) { + if (item.textPaths.length > 0) { + outlines = item.textPaths[0].texts[0].createOutlines(); + } + } else { + error("Be sure to use:\n Story | TextFrame | Paragraph | Line | Word | Character | TextPath") + return false; + } + + // process outlines + if (outlines.length != undefined && outlines.length === 1) { + // multi-line text from group array + if (outlines[0] instanceof Group) { + outlines = ungroup(outlines[0]); + if (cb instanceof Function) { + return forEach(outlines, cb); + } else { + return outlines; + } + } + + // single polygon + if (cb instanceof Function) { + cb(outlines[0]); + } else { + return outlines[0]; + } + } else { + + // collection of polygons + if (cb instanceof Function) { + return forEach(outlines, cb); + } else { + return outlines; + } + } +} + // ---------------------------------------- // Typography/Constants // ---------------------------------------- diff --git a/test/shape-tests.jsx b/test/shape-tests.jsx index 6f8f5133..5af969b4 100644 --- a/test/shape-tests.jsx +++ b/test/shape-tests.jsx @@ -77,6 +77,24 @@ basilTest("VertexTests", { assert(shape instanceof GraphicLine); assert(shape.paths.item(0).entirePath.length === 8); // because of overlapping points bug + }, + + testPathToPoints: function() { + textSize(100); + var myText = text("hello", 0, 0, width, height); + var outlines = createOutlines(myText); + var pts = pathToPoints(outlines); + assert(outlines instanceof Polygon); + assert(pts.paths.length === 7); + assert(pts.points instanceof Array); + assert(pts.beziers instanceof Array); + + var myEllipse = ellipse(width/2, height/2, width/2, width/2); + var pts = pathToPoints(myEllipse); + assert(pts.points.length === 4); + var ptsExtended = pathToPoints(myEllipse, 2); + assert(ptsExtended.points.length === 12); + } }); diff --git a/test/typography-tests.jsx b/test/typography-tests.jsx index 8a904e57..4a73ceab 100644 --- a/test/typography-tests.jsx +++ b/test/typography-tests.jsx @@ -319,6 +319,18 @@ basilTest("TypographyTests", { assert(myRect.appliedObjectStyle === objStyle); assert(myRect.topLeftCornerOption === CornerOptions.ROUNDED_CORNER); + }, + + testCreateOutlines: function() { + textSize(100); + var myText = text("hello", 0, 0, width, height); + var outlines = createOutlines(myText); + assert(outlines instanceof Polygon); + + var myText = text("hello \n world", 0, 0, width, height); + var outlines = createOutlines(myText); + assert(outlines instanceof Array); + } }); From 5451502f45e7e856126fd96d3f0e8a450fa99b62 Mon Sep 17 00:00:00 2001 From: ffd8 Date: Sun, 22 Mar 2020 20:57:25 +0100 Subject: [PATCH 2/9] pausing changes - returned core.js back to normal - modified shape description --- basil.js | 15 +++++++++------ src/includes/core.js | 2 +- src/includes/shape.js | 13 ++++++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/basil.js b/basil.js index 87f89f20..4a69e866 100644 --- a/basil.js +++ b/basil.js @@ -819,7 +819,7 @@ var getParentFunctionName = function(level) { } var checkNull = function (obj) { - if(obj === null || typeof obj === undefined) error("Received null " + obj); + if(obj === null || typeof obj === undefined) error("Received null object."); }; var isEnum = function(base, value) { @@ -7734,13 +7734,15 @@ pub.vertex = function() { /** * @summary Get points and bezier coordinates from path(s). - * @description Returns an object containing array of points, beziers, and both separated by paths. Intended for use with `createOutlines()` of text, but works with most pageItem paths. Accepts both single paths or a collection/group of paths. Both points and beziers will have connected lines (one big array), use paths followed by points or beziers for isolated shapes. + * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths of a given path item in InDesign. Together with `createOutlines()` this can be used on text items to retrieve points, beziers and paths of text items. Accepts both single paths or a collection/group of paths. + * When using this on a multi path object (e.g. text with separate paths), the `paths` property can be used to loop over every path separately, whereas the properties `points` and `beziers` contain arrays for all paths combined. + * An optional second parameter allows to add and return additional points between existing points, which is helpful for subdividing existing paths. * * @cat Shape * @method pathToPoints - * @param {Object} obj The pageItem(s) to process point/bezier coordinates of - * @param {Number} [addPts] Optional amount of additional interpolated points. Ignore if using beziers. - * @return {Object} Returns object with following values: .points[], .beziers[], .paths[].points[], .paths[].beziers[] + * @param {Object} obj The pageItem(s) to process point/bezier coordinates of. + * @param {Number} [addPts] Optional amount of additional interpolated points. + * @return {Object} Returns object with the following arrays `points`, `beziers`, `paths` * * @example Points * noFill(); @@ -7847,7 +7849,8 @@ pub.pathToPoints = function(obj, addPts) { pz.points.push(pzPoint) pzPoints.push(pzPoint); } else { - var nextPt = paths.pathPoints[(i + 1) % paths.pathPoints.length]; + var nextSel = (i + 1) % paths.pathPoints.length; + var nextPt = paths.pathPoints[nextSel]; var amt = 1.0 / (addPts + 1); for (var t = 0; t < 1; t += amt) { var ptStep = interpolateBezier(pt, nextPt, t); diff --git a/src/includes/core.js b/src/includes/core.js index 8ceff013..5a2cfa77 100644 --- a/src/includes/core.js +++ b/src/includes/core.js @@ -548,7 +548,7 @@ var getParentFunctionName = function(level) { } var checkNull = function (obj) { - if(obj === null || typeof obj === undefined) error("Received null " + obj); + if(obj === null || typeof obj === undefined) error("Received null object."); }; var isEnum = function(base, value) { diff --git a/src/includes/shape.js b/src/includes/shape.js index 91a625a7..a534dd52 100644 --- a/src/includes/shape.js +++ b/src/includes/shape.js @@ -553,13 +553,15 @@ pub.vertex = function() { /** * @summary Get points and bezier coordinates from path(s). - * @description Returns an object containing array of points, beziers, and both separated by paths. Intended for use with `createOutlines()` of text, but works with most pageItem paths. Accepts both single paths or a collection/group of paths. Both points and beziers will have connected lines (one big array), use paths followed by points or beziers for isolated shapes. + * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths of a given path item in InDesign. Together with `createOutlines()` this can be used on text items to retrieve points, beziers and paths of text items. Accepts both single paths or a collection/group of paths. + * When using this on a multi path object (e.g. text with separate paths), the `paths` property can be used to loop over every path separately, whereas the properties `points` and `beziers` contain arrays for all paths combined. + * An optional second parameter allows to add and return additional points between existing points, which is helpful for subdividing existing paths. * * @cat Shape * @method pathToPoints - * @param {Object} obj The pageItem(s) to process point/bezier coordinates of - * @param {Number} [addPts] Optional amount of additional interpolated points. Ignore if using beziers. - * @return {Object} Returns object with following values: .points[], .beziers[], .paths[].points[], .paths[].beziers[] + * @param {Object} obj The pageItem(s) to process point/bezier coordinates of. + * @param {Number} [addPts] Optional amount of additional interpolated points. + * @return {Object} Returns object with the following arrays `points`, `beziers`, `paths` * * @example Points * noFill(); @@ -666,7 +668,8 @@ pub.pathToPoints = function(obj, addPts) { pz.points.push(pzPoint) pzPoints.push(pzPoint); } else { - var nextPt = paths.pathPoints[(i + 1) % paths.pathPoints.length]; + var nextSel = (i + 1) % paths.pathPoints.length; + var nextPt = paths.pathPoints[nextSel]; var amt = 1.0 / (addPts + 1); for (var t = 0; t < 1; t += amt) { var ptStep = interpolateBezier(pt, nextPt, t); From 85f976e708a54d162dff82b61e7025a183689479 Mon Sep 17 00:00:00 2001 From: ffd8 Date: Mon, 23 Mar 2020 01:30:44 +0100 Subject: [PATCH 3/9] review revisions - updated descriptions - reduced examples to minimum #### pathToPoints() - fixed open path items w/ `addPts` #### createOutlines() - fixed no fill/stroke text with - fixed issue with linked textFrames - always returns array of polygons --- basil.js | 213 +++++++++++++++---------------------- src/includes/shape.js | 101 +++++------------- src/includes/typography.js | 114 +++++++++++--------- 3 files changed, 177 insertions(+), 251 deletions(-) diff --git a/basil.js b/basil.js index 4a69e866..b86935c2 100644 --- a/basil.js +++ b/basil.js @@ -7734,7 +7734,7 @@ pub.vertex = function() { /** * @summary Get points and bezier coordinates from path(s). - * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths of a given path item in InDesign. Together with `createOutlines()` this can be used on text items to retrieve points, beziers and paths of text items. Accepts both single paths or a collection/group of paths. + * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths of a given path item in InDesign. Together with `createOutlines()` this can be used on text items. Accepts both single paths or a collection/group of paths. * When using this on a multi path object (e.g. text with separate paths), the `paths` property can be used to loop over every path separately, whereas the properties `points` and `beziers` contain arrays for all paths combined. * An optional second parameter allows to add and return additional points between existing points, which is helpful for subdividing existing paths. * @@ -7745,81 +7745,25 @@ pub.vertex = function() { * @return {Object} Returns object with the following arrays `points`, `beziers`, `paths` * * @example Points - * noFill(); - * var myEllipse = ellipse(width / 2, height / 2, width / 2, width / 2); - * var pts = pathToPoints(myEllipse); - * var ptsExtended = pathToPoints(myEllipse, 5); // add 5 points between points - * - * noStroke(); - * fill(0, 0, 255); - * - * // draw normal points - * for (var i=0; i < pts.points.length; i++) { - * var pt = pts.points[i]; - * ellipse(pt.x, pt.y, 10, 10); - * } - * - * noFill(); - * stroke(255, 0, 0); - * // draw extended points - * for (var i=0; i < ptsExtended.points.length; i++) { - * var pt = ptsExtended.points[i]; - * ellipse(pt.x, pt.y, 10, 10); - * } - * - * @example Beziers from text using createOutlines() - * textSize(400); - * var myText = text("H", width/4, height/4, 500, 500); - * var outlines = createOutlines(myText); - * var pts = pathToPoints(outlines); - * property(outlines, "fillColor", "None"); - * - * noFill(); - * stroke(0, 255, 0); - * - * // draw bezier - * beginShape(); - * for (var i=0; i < pts.beziers.length; i++) { - * var pt = pts.beziers[i]; // point w/ .anchor .left .right - * vertex(pt.anchor.x, pt.anchor.y, pt.left.x, pt.left.y, pt.right.x, pt.right.y); - * } - * endShape(CLOSE); - * - * // debug bezier - * for (var i=0; i < pts.beziers.length; i++) { - * var pt = pts.beziers[i]; // point - * var anchor = pt.anchor; // anchor - * var left = pt.left; // left handle - * var right = pt.right; // right handle - * noStroke(); - * fill(0); - * ellipse(anchor.x, anchor.y, 5, 5); - * ellipse(left.x, left.y, 5, 5); - * ellipse(right.x, right.y, 5, 5); - * stroke(0, 255, 0); - * line(anchor.x, anchor.y, left.x, left.y); - * line(anchor.x, anchor.y, right.x, right.y); - * } - * - * @example Isolated Paths of Beziers from text using createOutlines() - * textSize(200); - * var myText = text("hello", width/4, height/4, 500, 500); - * var outlines = createOutlines(myText); - * var pts = pathToPoints(outlines); - * property(outlines, "fillColor", "None"); - * - * noFill(); - * stroke(0, 255, 0); - * - * // just beziers - * for (var p=0; p < pts.paths.length; p++) { - * var path = pts.paths[p]; // each path isolated - * beginShape(); - * for (var i=0; iPoints w/ Interpolation + * var pts = pathToPoints(obj, 5); // adds 5 points between points + * println(pts.points.length); # of points + * + * @example Beziers + * var pts = pathToPoints(obj); + * println(pts.beziers.length); # of beziers + * // vertex(pts.beziers[0].anchor.x, pts.beziers[0].anchor.y, pts.beziers[0].left.x, pts.beziers[0].left.y, pts.beziers[0].right.x, pts.beziers[0].right.y); + * + * @example Isolated Paths of Points + * var pts = pathToPoints(obj); + * for (var i=0; i < pts.paths.length; i++) { + * var path = pts.paths[i]; + * println(path.points.length); # of points + * point(path.points[0].x, path.points[0].y); // first point * } * */ @@ -7852,6 +7796,11 @@ pub.pathToPoints = function(obj, addPts) { var nextSel = (i + 1) % paths.pathPoints.length; var nextPt = paths.pathPoints[nextSel]; var amt = 1.0 / (addPts + 1); + + if (formElm.paths[0].pathType === PathType.OPEN_PATH && i === paths.pathPoints.length-1) { + amt = 1; // don't interpolate end to start on open paths + } + for (var t = 0; t < 1; t += amt) { var ptStep = interpolateBezier(pt, nextPt, t); var pzPointStep = {x:ptStep.x, y:ptStep.y}; @@ -9064,86 +9013,90 @@ pub.paragraphStyle = function(textOrName, props) { /** * @summary Convert text items to outlines. - * @description Returns a polygon or array of polygons (if text is multi-line, multi-boxed, or a textPath) and optionally processes them with a callback function. Intended for use with `pathToPoints()` for getting point and bezier coordinates from outlines. Warning, InDesign requires that text have a fill color in order to createOutlines. + * @description Returns a polygon or array of polygons (if text is multi-line, multi-boxed, or a textPath) and optionally processes them with a callback function. + * Use together with `pathToPoints()` for getting point and bezier coordinates from outlines. * * @cat Typography * @method createOutlines * @param {Story|TextFrame||Paragraph||Line||Word||Character||GraphicLine||Polygon||Oval||Rectangle} item Text or textpath to be outlined. * @param {Function} [cb] Optional: The callback function to call with each polygon. Passed arguments: `obj`, `loopCounter` - * @return {Polygon | Array of Polygons} Returns a single or array of polygons depending on text converted. - * - * @example Points - * textSize(150); - * var myText = text("Hello", width/4, height/4, 500, 800); - * var outlines = createOutlines(myText); // create outlines - * var pts = pathToPoints(outlines); // get points - * // inspect(pts); // view structure - * for (var i=0; i < pts.points.length; i++) { - * var pt = pts.points[i]; // each point - * ellipse(pt.x, pt.y, 5, 5); - * } - * property(outlines, "fillColor", "None"); + * @return {Array of Polygons} Returns an array of polygons. * - * @example Points Extended + * @example createOutlines * textSize(150); - * var myText = text("Hello", width/4, height/4, 500, 800); - * var outlines = createOutlines(myText); // create outlines - * var pts = pathToPoints(outlines, 4); // get points, add 4 between each point - * for (var i=0; i < pts.points.length; i++) { - * var pt = pts.points[i]; // each point - * var s = map(i, 0, pts.points.length, .1, 5); - * ellipse(pt.x, pt.y, s, s); - * } - * property(outlines, "fillColor", "None"); + * var myText = text("Hello", 0, 0, width, height); + * var outlines = createOutlines(myText); * - * @example Path isolated Beziers + * @example createOutlines with Callback * textSize(150); - * var myText = text("Hello", width/4, height/4, 500, 800); - * var outlines = createOutlines(myText); // create outlines - * var pts = pathToPoints(outlines); // get points + beziers + paths - * property(outlines, "fillColor", "None"); - * - * noFill(); - * stroke(0); - * - * // process paths -> beziers - * for (var p=0; p < pts.paths.length; p++) { - * var path = pts.paths[p]; // each path isolated - * beginShape(); - * for (var i=0; i < path.beziers.length; i++) { - * var tp = path.beziers[i]; - * var offset = sin(i * 5) * 15; // shift points - * vertex(tp.anchor.x + offset, tp.anchor.y, tp.left.x, tp.left.y, tp.right.x, tp.right.y); - * } - * endShape(CLOSE); - * } + * var myText = text("Hello \nWorld", 0, 0, width, height); + * var outlines = createOutlines(myText, function(obj){ + * var pts = pathToPoints(obj); + * }); * */ pub.createOutlines = function(item, cb) { - var outlines; + var outlines, changedFill = false, debugCO = false; - // check textFrames, textPaths, or neither + // check if textFrames if (item.hasOwnProperty('createOutlines')) { + // gather whole texts (for linked textFrames) + item = item.parentStory.texts[0]; + + // catch text w/o fill or stroke + for (var i=0; i < item.texts.length; i++) { + if (item.texts[i].fillColor.name === "None" && item.texts[i].strokeColor.name === "None") { + item.texts[i].fillColor = color(0); + changedFill = true; + } + } + inspect(item) outlines = item.createOutlines(); + + // check if textPath } else if ( item instanceof GraphicLine || item instanceof Oval || item instanceof Rectangle || item instanceof Polygon) { + // check shape has textPaths if (item.textPaths.length > 0) { - outlines = item.textPaths[0].texts[0].createOutlines(); + + item = item.textPaths[0].parentStory.texts[0]; + + // catch text w/o fill or stroke + for (var i=0; i < item.texts.length; i++) { + if (item.texts[i].fillColor.name === "None" && item.texts[i].strokeColor.name === "None") { + item.texts[i].fillColor = color(0); + changedFill = true; + } + } + + // *** how to remove textPath holder object?? + outlines = item.createOutlines(); } + + // error if neither } else { error("Be sure to use:\n Story | TextFrame | Paragraph | Line | Word | Character | TextPath") return false; } + // remove tempFill for those items + if(changedFill){ + for(var i=0; i< outlines.length; i++){ + outlines[i].fillColor = "None"; + } + + } + // process outlines if (outlines.length != undefined && outlines.length === 1) { // multi-line text from group array if (outlines[0] instanceof Group) { outlines = ungroup(outlines[0]); + if (debugCO) println('group polygons'); // textframe multi-line if (cb instanceof Function) { return forEach(outlines, cb); } else { @@ -9152,14 +9105,24 @@ pub.createOutlines = function(item, cb) { } // single polygon + if (debugCO) println('solo polygons/groups'); // single normal textFrame 1 line if (cb instanceof Function) { - cb(outlines[0]); + return forEach(outlines, cb); } else { - return outlines[0]; + return outlines; } } else { + if (debugCO) println('array of polygons/groups'); // textPath, linked textFrames // collection of polygons + if (outlines[0] instanceof Group) { + outlinesGroups = []; + for(var i=0; i < outlines.length; i++){ + outlinesGroups.push(ungroup(outlines[i])); + } + outlines = outlinesGroups; + } + if (cb instanceof Function) { return forEach(outlines, cb); } else { diff --git a/src/includes/shape.js b/src/includes/shape.js index a534dd52..57b45751 100644 --- a/src/includes/shape.js +++ b/src/includes/shape.js @@ -553,7 +553,7 @@ pub.vertex = function() { /** * @summary Get points and bezier coordinates from path(s). - * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths of a given path item in InDesign. Together with `createOutlines()` this can be used on text items to retrieve points, beziers and paths of text items. Accepts both single paths or a collection/group of paths. + * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths of a given path item in InDesign. Together with `createOutlines()` this can be used on text items. Accepts both single paths or a collection/group of paths. * When using this on a multi path object (e.g. text with separate paths), the `paths` property can be used to loop over every path separately, whereas the properties `points` and `beziers` contain arrays for all paths combined. * An optional second parameter allows to add and return additional points between existing points, which is helpful for subdividing existing paths. * @@ -564,81 +564,25 @@ pub.vertex = function() { * @return {Object} Returns object with the following arrays `points`, `beziers`, `paths` * * @example Points - * noFill(); - * var myEllipse = ellipse(width / 2, height / 2, width / 2, width / 2); - * var pts = pathToPoints(myEllipse); - * var ptsExtended = pathToPoints(myEllipse, 5); // add 5 points between points - * - * noStroke(); - * fill(0, 0, 255); - * - * // draw normal points - * for (var i=0; i < pts.points.length; i++) { - * var pt = pts.points[i]; - * ellipse(pt.x, pt.y, 10, 10); - * } - * - * noFill(); - * stroke(255, 0, 0); - * // draw extended points - * for (var i=0; i < ptsExtended.points.length; i++) { - * var pt = ptsExtended.points[i]; - * ellipse(pt.x, pt.y, 10, 10); - * } - * - * @example Beziers from text using createOutlines() - * textSize(400); - * var myText = text("H", width/4, height/4, 500, 500); - * var outlines = createOutlines(myText); - * var pts = pathToPoints(outlines); - * property(outlines, "fillColor", "None"); - * - * noFill(); - * stroke(0, 255, 0); - * - * // draw bezier - * beginShape(); - * for (var i=0; i < pts.beziers.length; i++) { - * var pt = pts.beziers[i]; // point w/ .anchor .left .right - * vertex(pt.anchor.x, pt.anchor.y, pt.left.x, pt.left.y, pt.right.x, pt.right.y); - * } - * endShape(CLOSE); - * - * // debug bezier - * for (var i=0; i < pts.beziers.length; i++) { - * var pt = pts.beziers[i]; // point - * var anchor = pt.anchor; // anchor - * var left = pt.left; // left handle - * var right = pt.right; // right handle - * noStroke(); - * fill(0); - * ellipse(anchor.x, anchor.y, 5, 5); - * ellipse(left.x, left.y, 5, 5); - * ellipse(right.x, right.y, 5, 5); - * stroke(0, 255, 0); - * line(anchor.x, anchor.y, left.x, left.y); - * line(anchor.x, anchor.y, right.x, right.y); - * } - * - * @example Isolated Paths of Beziers from text using createOutlines() - * textSize(200); - * var myText = text("hello", width/4, height/4, 500, 500); - * var outlines = createOutlines(myText); - * var pts = pathToPoints(outlines); - * property(outlines, "fillColor", "None"); - * - * noFill(); - * stroke(0, 255, 0); - * - * // just beziers - * for (var p=0; p < pts.paths.length; p++) { - * var path = pts.paths[p]; // each path isolated - * beginShape(); - * for (var i=0; iPoints w/ Interpolation + * var pts = pathToPoints(obj, 5); // adds 5 points between points + * println(pts.points.length); # of points + * + * @example Beziers + * var pts = pathToPoints(obj); + * println(pts.beziers.length); # of beziers + * // vertex(pts.beziers[0].anchor.x, pts.beziers[0].anchor.y, pts.beziers[0].left.x, pts.beziers[0].left.y, pts.beziers[0].right.x, pts.beziers[0].right.y); + * + * @example Isolated Paths of Points + * var pts = pathToPoints(obj); + * for (var i=0; i < pts.paths.length; i++) { + * var path = pts.paths[i]; + * println(path.points.length); # of points + * point(path.points[0].x, path.points[0].y); // first point * } * */ @@ -671,6 +615,11 @@ pub.pathToPoints = function(obj, addPts) { var nextSel = (i + 1) % paths.pathPoints.length; var nextPt = paths.pathPoints[nextSel]; var amt = 1.0 / (addPts + 1); + + if (formElm.paths[0].pathType === PathType.OPEN_PATH && i === paths.pathPoints.length-1) { + amt = 1; // don't interpolate end to start on open paths + } + for (var t = 0; t < 1; t += amt) { var ptStep = interpolateBezier(pt, nextPt, t); var pzPointStep = {x:ptStep.x, y:ptStep.y}; diff --git a/src/includes/typography.js b/src/includes/typography.js index 5615ac9a..226bd0a7 100644 --- a/src/includes/typography.js +++ b/src/includes/typography.js @@ -485,86 +485,90 @@ pub.paragraphStyle = function(textOrName, props) { /** * @summary Convert text items to outlines. - * @description Returns a polygon or array of polygons (if text is multi-line, multi-boxed, or a textPath) and optionally processes them with a callback function. Intended for use with `pathToPoints()` for getting point and bezier coordinates from outlines. Warning, InDesign requires that text have a fill color in order to createOutlines. + * @description Returns a polygon or array of polygons (if text is multi-line, multi-boxed, or a textPath) and optionally processes them with a callback function. + * Use together with `pathToPoints()` for getting point and bezier coordinates from outlines. * * @cat Typography * @method createOutlines * @param {Story|TextFrame||Paragraph||Line||Word||Character||GraphicLine||Polygon||Oval||Rectangle} item Text or textpath to be outlined. * @param {Function} [cb] Optional: The callback function to call with each polygon. Passed arguments: `obj`, `loopCounter` - * @return {Polygon | Array of Polygons} Returns a single or array of polygons depending on text converted. + * @return {Array of Polygons} Returns an array of polygons. * - * @example Points + * @example createOutlines * textSize(150); - * var myText = text("Hello", width/4, height/4, 500, 800); - * var outlines = createOutlines(myText); // create outlines - * var pts = pathToPoints(outlines); // get points - * // inspect(pts); // view structure - * for (var i=0; i < pts.points.length; i++) { - * var pt = pts.points[i]; // each point - * ellipse(pt.x, pt.y, 5, 5); - * } - * property(outlines, "fillColor", "None"); - * - * @example Points Extended - * textSize(150); - * var myText = text("Hello", width/4, height/4, 500, 800); - * var outlines = createOutlines(myText); // create outlines - * var pts = pathToPoints(outlines, 4); // get points, add 4 between each point - * for (var i=0; i < pts.points.length; i++) { - * var pt = pts.points[i]; // each point - * var s = map(i, 0, pts.points.length, .1, 5); - * ellipse(pt.x, pt.y, s, s); - * } - * property(outlines, "fillColor", "None"); - * - * @example Path isolated Beziers + * var myText = text("Hello", 0, 0, width, height); + * var outlines = createOutlines(myText); + * + * @example createOutlines with Callback * textSize(150); - * var myText = text("Hello", width/4, height/4, 500, 800); - * var outlines = createOutlines(myText); // create outlines - * var pts = pathToPoints(outlines); // get points + beziers + paths - * property(outlines, "fillColor", "None"); - * - * noFill(); - * stroke(0); - * - * // process paths -> beziers - * for (var p=0; p < pts.paths.length; p++) { - * var path = pts.paths[p]; // each path isolated - * beginShape(); - * for (var i=0; i < path.beziers.length; i++) { - * var tp = path.beziers[i]; - * var offset = sin(i * 5) * 15; // shift points - * vertex(tp.anchor.x + offset, tp.anchor.y, tp.left.x, tp.left.y, tp.right.x, tp.right.y); - * } - * endShape(CLOSE); - * } + * var myText = text("Hello \nWorld", 0, 0, width, height); + * var outlines = createOutlines(myText, function(obj){ + * var pts = pathToPoints(obj); + * }); * */ pub.createOutlines = function(item, cb) { - var outlines; + var outlines, changedFill = false, debugCO = false; - // check textFrames, textPaths, or neither + // check if textFrames if (item.hasOwnProperty('createOutlines')) { + // gather whole texts (for linked textFrames) + item = item.parentStory.texts[0]; + + // catch text w/o fill or stroke + for (var i=0; i < item.texts.length; i++) { + if (item.texts[i].fillColor.name === "None" && item.texts[i].strokeColor.name === "None") { + item.texts[i].fillColor = color(0); + changedFill = true; + } + } + inspect(item) outlines = item.createOutlines(); + + // check if textPath } else if ( item instanceof GraphicLine || item instanceof Oval || item instanceof Rectangle || item instanceof Polygon) { + // check shape has textPaths if (item.textPaths.length > 0) { - outlines = item.textPaths[0].texts[0].createOutlines(); + + item = item.textPaths[0].parentStory.texts[0]; + + // catch text w/o fill or stroke + for (var i=0; i < item.texts.length; i++) { + if (item.texts[i].fillColor.name === "None" && item.texts[i].strokeColor.name === "None") { + item.texts[i].fillColor = color(0); + changedFill = true; + } + } + + // *** how to remove textPath holder object?? + outlines = item.createOutlines(); } + + // error if neither } else { error("Be sure to use:\n Story | TextFrame | Paragraph | Line | Word | Character | TextPath") return false; } + // remove tempFill for those items + if(changedFill){ + for(var i=0; i< outlines.length; i++){ + outlines[i].fillColor = "None"; + } + + } + // process outlines if (outlines.length != undefined && outlines.length === 1) { // multi-line text from group array if (outlines[0] instanceof Group) { outlines = ungroup(outlines[0]); + if (debugCO) println('group polygons'); // textframe multi-line if (cb instanceof Function) { return forEach(outlines, cb); } else { @@ -573,14 +577,24 @@ pub.createOutlines = function(item, cb) { } // single polygon + if (debugCO) println('solo polygons/groups'); // single normal textFrame 1 line if (cb instanceof Function) { - cb(outlines[0]); + return forEach(outlines, cb); } else { - return outlines[0]; + return outlines; } } else { + if (debugCO) println('array of polygons/groups'); // textPath, linked textFrames // collection of polygons + if (outlines[0] instanceof Group) { + outlinesGroups = []; + for(var i=0; i < outlines.length; i++){ + outlinesGroups.push(ungroup(outlines[i])); + } + outlines = outlinesGroups; + } + if (cb instanceof Function) { return forEach(outlines, cb); } else { From bb7c6d0195ed005c7fafea8f4d3eb68edf2ff266 Mon Sep 17 00:00:00 2001 From: ffd8 Date: Mon, 23 Mar 2020 01:42:46 +0100 Subject: [PATCH 4/9] disable inspect .. left on while testing.. --- basil.js | 2 +- src/includes/typography.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/basil.js b/basil.js index b86935c2..c185bb5b 100644 --- a/basil.js +++ b/basil.js @@ -9052,7 +9052,7 @@ pub.createOutlines = function(item, cb) { changedFill = true; } } - inspect(item) + outlines = item.createOutlines(); // check if textPath diff --git a/src/includes/typography.js b/src/includes/typography.js index 226bd0a7..1be2e6bc 100644 --- a/src/includes/typography.js +++ b/src/includes/typography.js @@ -524,7 +524,7 @@ pub.createOutlines = function(item, cb) { changedFill = true; } } - inspect(item) + outlines = item.createOutlines(); // check if textPath From 7c2a95baf794a2d49c3da7ee863db60c3f582164 Mon Sep 17 00:00:00 2001 From: ffd8 Date: Mon, 23 Mar 2020 16:51:30 +0100 Subject: [PATCH 5/9] updated description + examples clean up of wording/formatting for docs + made simpler minimal examples. For `pathToPoints()` I think it's still necessary to give small `for` loop examples for a beginner to know just how to process the object. --- basil.js | 48 +++++++++++++++++++++++++++----------- src/includes/shape.js | 37 ++++++++++++++++++++++------- src/includes/typography.js | 11 +++++---- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/basil.js b/basil.js index c185bb5b..1647eb5a 100644 --- a/basil.js +++ b/basil.js @@ -7734,41 +7734,60 @@ pub.vertex = function() { /** * @summary Get points and bezier coordinates from path(s). - * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths of a given path item in InDesign. Together with `createOutlines()` this can be used on text items. Accepts both single paths or a collection/group of paths. + * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths (containing its array of points + beziers) of a given pageItem in InDesign. Together with `createOutlines()` this can be used on text items. Accepts both single paths or a collection/group of paths. * When using this on a multi path object (e.g. text with separate paths), the `paths` property can be used to loop over every path separately, whereas the properties `points` and `beziers` contain arrays for all paths combined. * An optional second parameter allows to add and return additional points between existing points, which is helpful for subdividing existing paths. * * @cat Shape * @method pathToPoints * @param {Object} obj The pageItem(s) to process point/bezier coordinates of. - * @param {Number} [addPts] Optional amount of additional interpolated points. + * @param {Number} [addPoints] Optional amount of additional interpolated points. * @return {Object} Returns object with the following arrays `points`, `beziers`, `paths` * * @example Points * var pts = pathToPoints(obj); * println(pts.points.length); // # of points - * point(pts.points[0].x, pts.points[0].y); // first point + * + * for (var i = 0; i < pts.points.length; i++) { + * var pt = pts.points[i]; + * point(pt.x, pt.y); + * } * * @example Points w/ Interpolation * var pts = pathToPoints(obj, 5); // adds 5 points between points * println(pts.points.length); # of points * + * for (var i = 0; i < pts.points.length; i++) { + * var pt = pts.points[i]; + * point(pt.x, pt.y); + * } + * * @example Beziers * var pts = pathToPoints(obj); * println(pts.beziers.length); # of beziers - * // vertex(pts.beziers[0].anchor.x, pts.beziers[0].anchor.y, pts.beziers[0].left.x, pts.beziers[0].left.y, pts.beziers[0].right.x, pts.beziers[0].right.y); + * + * beginShape(); + * for (var i = 0; i < pts.beziers.length; i++) { + * var bz = pts.beziers[i]; + * vertex(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y, bz.right.x, bz.right.y); + * } + * endShape(CLOSE); * * @example Isolated Paths of Points * var pts = pathToPoints(obj); + * * for (var i=0; i < pts.paths.length; i++) { * var path = pts.paths[i]; * println(path.points.length); # of points - * point(path.points[0].x, path.points[0].y); // first point - * } * + * for (var i = 0; i < path.points.length; i++) { + * var pt = path.points[i]; + * point(pt.x, pt.y); + * } + * } */ -pub.pathToPoints = function(obj, addPts) { +pub.pathToPoints = function(obj, addPoints) { var pz = {paths:[], points:[], beziers:[]}, pzGroup = false, grabPoints = function(formElm) { @@ -7788,14 +7807,14 @@ pub.pathToPoints = function(obj, addPts) { pzBeziers.push(pzBezier) // optionally interpolated points - if (addPts === undefined) { + if (addPoints === undefined) { var pzPoint = {x:pt.anchor[0], y:pt.anchor[1]}; pz.points.push(pzPoint) pzPoints.push(pzPoint); } else { var nextSel = (i + 1) % paths.pathPoints.length; var nextPt = paths.pathPoints[nextSel]; - var amt = 1.0 / (addPts + 1); + var amt = 1.0 / (addPoints + 1); if (formElm.paths[0].pathType === PathType.OPEN_PATH && i === paths.pathPoints.length-1) { amt = 1; // don't interpolate end to start on open paths @@ -9013,21 +9032,22 @@ pub.paragraphStyle = function(textOrName, props) { /** * @summary Convert text items to outlines. - * @description Returns a polygon or array of polygons (if text is multi-line, multi-boxed, or a textPath) and optionally processes them with a callback function. + * @description Returns an array of polygons after outlining text and optionally processes them with a callback function. * Use together with `pathToPoints()` for getting point and bezier coordinates from outlines. * * @cat Typography * @method createOutlines - * @param {Story|TextFrame||Paragraph||Line||Word||Character||GraphicLine||Polygon||Oval||Rectangle} item Text or textpath to be outlined. - * @param {Function} [cb] Optional: The callback function to call with each polygon. Passed arguments: `obj`, `loopCounter` + * + * @param {TextFrame|TextPath} item Text or TextPath to be outlined. + * @param {Function} [cb] Optional: Callback function to use with each polygon. Passed arguments: `obj`, `loopCounter` * @return {Array of Polygons} Returns an array of polygons. * - * @example createOutlines + * @example * textSize(150); * var myText = text("Hello", 0, 0, width, height); * var outlines = createOutlines(myText); * - * @example createOutlines with Callback + * @example w/ Callback * textSize(150); * var myText = text("Hello \nWorld", 0, 0, width, height); * var outlines = createOutlines(myText, function(obj){ diff --git a/src/includes/shape.js b/src/includes/shape.js index 57b45751..375deb41 100644 --- a/src/includes/shape.js +++ b/src/includes/shape.js @@ -553,41 +553,60 @@ pub.vertex = function() { /** * @summary Get points and bezier coordinates from path(s). - * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths of a given path item in InDesign. Together with `createOutlines()` this can be used on text items. Accepts both single paths or a collection/group of paths. + * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths (containing its array of points + beziers) of a given pageItem in InDesign. Together with `createOutlines()` this can be used on text items. Accepts both single paths or a collection/group of paths. * When using this on a multi path object (e.g. text with separate paths), the `paths` property can be used to loop over every path separately, whereas the properties `points` and `beziers` contain arrays for all paths combined. * An optional second parameter allows to add and return additional points between existing points, which is helpful for subdividing existing paths. * * @cat Shape * @method pathToPoints * @param {Object} obj The pageItem(s) to process point/bezier coordinates of. - * @param {Number} [addPts] Optional amount of additional interpolated points. + * @param {Number} [addPoints] Optional amount of additional interpolated points. * @return {Object} Returns object with the following arrays `points`, `beziers`, `paths` * * @example Points * var pts = pathToPoints(obj); * println(pts.points.length); // # of points - * point(pts.points[0].x, pts.points[0].y); // first point + * + * for (var i = 0; i < pts.points.length; i++) { + * var pt = pts.points[i]; + * point(pt.x, pt.y); + * } * * @example Points w/ Interpolation * var pts = pathToPoints(obj, 5); // adds 5 points between points * println(pts.points.length); # of points * + * for (var i = 0; i < pts.points.length; i++) { + * var pt = pts.points[i]; + * point(pt.x, pt.y); + * } + * * @example Beziers * var pts = pathToPoints(obj); * println(pts.beziers.length); # of beziers - * // vertex(pts.beziers[0].anchor.x, pts.beziers[0].anchor.y, pts.beziers[0].left.x, pts.beziers[0].left.y, pts.beziers[0].right.x, pts.beziers[0].right.y); + * + * beginShape(); + * for (var i = 0; i < pts.beziers.length; i++) { + * var bz = pts.beziers[i]; + * vertex(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y, bz.right.x, bz.right.y); + * } + * endShape(CLOSE); * * @example Isolated Paths of Points * var pts = pathToPoints(obj); + * * for (var i=0; i < pts.paths.length; i++) { * var path = pts.paths[i]; * println(path.points.length); # of points - * point(path.points[0].x, path.points[0].y); // first point - * } * + * for (var i = 0; i < path.points.length; i++) { + * var pt = path.points[i]; + * point(pt.x, pt.y); + * } + * } */ -pub.pathToPoints = function(obj, addPts) { +pub.pathToPoints = function(obj, addPoints) { var pz = {paths:[], points:[], beziers:[]}, pzGroup = false, grabPoints = function(formElm) { @@ -607,14 +626,14 @@ pub.pathToPoints = function(obj, addPts) { pzBeziers.push(pzBezier) // optionally interpolated points - if (addPts === undefined) { + if (addPoints === undefined) { var pzPoint = {x:pt.anchor[0], y:pt.anchor[1]}; pz.points.push(pzPoint) pzPoints.push(pzPoint); } else { var nextSel = (i + 1) % paths.pathPoints.length; var nextPt = paths.pathPoints[nextSel]; - var amt = 1.0 / (addPts + 1); + var amt = 1.0 / (addPoints + 1); if (formElm.paths[0].pathType === PathType.OPEN_PATH && i === paths.pathPoints.length-1) { amt = 1; // don't interpolate end to start on open paths diff --git a/src/includes/typography.js b/src/includes/typography.js index 1be2e6bc..5f1b2c47 100644 --- a/src/includes/typography.js +++ b/src/includes/typography.js @@ -485,21 +485,22 @@ pub.paragraphStyle = function(textOrName, props) { /** * @summary Convert text items to outlines. - * @description Returns a polygon or array of polygons (if text is multi-line, multi-boxed, or a textPath) and optionally processes them with a callback function. + * @description Returns an array of polygons after outlining text and optionally processes them with a callback function. * Use together with `pathToPoints()` for getting point and bezier coordinates from outlines. * * @cat Typography * @method createOutlines - * @param {Story|TextFrame||Paragraph||Line||Word||Character||GraphicLine||Polygon||Oval||Rectangle} item Text or textpath to be outlined. - * @param {Function} [cb] Optional: The callback function to call with each polygon. Passed arguments: `obj`, `loopCounter` + * + * @param {TextFrame|TextPath} item Text or TextPath to be outlined. + * @param {Function} [cb] Optional: Callback function to use with each polygon. Passed arguments: `obj`, `loopCounter` * @return {Array of Polygons} Returns an array of polygons. * - * @example createOutlines + * @example * textSize(150); * var myText = text("Hello", 0, 0, width, height); * var outlines = createOutlines(myText); * - * @example createOutlines with Callback + * @example w/ Callback * textSize(150); * var myText = text("Hello \nWorld", 0, 0, width, height); * var outlines = createOutlines(myText, function(obj){ From 0cae7ba2fea2f932a27e5d857ba2fd0feb216b08 Mon Sep 17 00:00:00 2001 From: ffd8 Date: Mon, 23 Mar 2020 16:59:49 +0100 Subject: [PATCH 6/9] changelog regarding textSize() added small update to `textSize()` to changelog --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index d4492d51..dfe357cd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -111,6 +111,7 @@ The user is allowed to have his files wherever he wants. * bounds() now correctly measures bounds in different canvas modes(MARGIN/BLEED etc.) * loadJSON(), loadString(), loadStrings() when passed a URL, can receive custom user-agent as 2nd parameter * weekday() now returns number day of the week (0 - 6) +* textSize() now returns helpful error if given size 0 or below - b.go() and b.loop() are removed (use function draw() or function loop() instead) From de582936ecb68dde14bb86eadf9d379a2277e906 Mon Sep 17 00:00:00 2001 From: ffd8 Date: Thu, 2 Apr 2020 23:45:42 +0200 Subject: [PATCH 7/9] temp remove of textSize() from changelog - ran into strange issue with current dev branch --- changelog.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index dfe357cd..1829131e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -111,8 +111,6 @@ The user is allowed to have his files wherever he wants. * bounds() now correctly measures bounds in different canvas modes(MARGIN/BLEED etc.) * loadJSON(), loadString(), loadStrings() when passed a URL, can receive custom user-agent as 2nd parameter * weekday() now returns number day of the week (0 - 6) -* textSize() now returns helpful error if given size 0 or below - - b.go() and b.loop() are removed (use function draw() or function loop() instead) - b.itemX(), b.itemY(), b.itemWidth(), b.itemHeight(), b.itemPosition() and b.itemSize() are removed From feea354511ba4652a85c472f0cea91db04ec099b Mon Sep 17 00:00:00 2001 From: ffd8 Date: Sat, 4 Apr 2020 14:58:43 +0200 Subject: [PATCH 8/9] typos in examples - forgot some inline comment slashes - untested nested loop was using same var --- basil.js | 15 ++++++++------- src/includes/shape.js | 15 ++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/basil.js b/basil.js index 1647eb5a..2b00b9bb 100644 --- a/basil.js +++ b/basil.js @@ -7755,7 +7755,7 @@ pub.vertex = function() { * * @example Points w/ Interpolation * var pts = pathToPoints(obj, 5); // adds 5 points between points - * println(pts.points.length); # of points + * println(pts.points.length); // # of points * * for (var i = 0; i < pts.points.length; i++) { * var pt = pts.points[i]; @@ -7764,7 +7764,7 @@ pub.vertex = function() { * * @example Beziers * var pts = pathToPoints(obj); - * println(pts.beziers.length); # of beziers + * println(pts.beziers.length); // # of beziers * * beginShape(); * for (var i = 0; i < pts.beziers.length; i++) { @@ -7774,16 +7774,17 @@ pub.vertex = function() { * endShape(CLOSE); * * @example Isolated Paths of Points - * var pts = pathToPoints(obj); + * var pts = pathToPoints(outlines, 3); // add 3 for more detail * - * for (var i=0; i < pts.paths.length; i++) { - * var path = pts.paths[i]; - * println(path.points.length); # of points + * for (var j=0; j < pts.paths.length; j++) { + * var path = pts.paths[j]; * + * beginShape(); * for (var i = 0; i < path.points.length; i++) { * var pt = path.points[i]; - * point(pt.x, pt.y); + * vertex(pt.x + random(5), pt.y); * } + * endShape(CLOSE); * } */ diff --git a/src/includes/shape.js b/src/includes/shape.js index 375deb41..4f585c19 100644 --- a/src/includes/shape.js +++ b/src/includes/shape.js @@ -574,7 +574,7 @@ pub.vertex = function() { * * @example Points w/ Interpolation * var pts = pathToPoints(obj, 5); // adds 5 points between points - * println(pts.points.length); # of points + * println(pts.points.length); // # of points * * for (var i = 0; i < pts.points.length; i++) { * var pt = pts.points[i]; @@ -583,7 +583,7 @@ pub.vertex = function() { * * @example Beziers * var pts = pathToPoints(obj); - * println(pts.beziers.length); # of beziers + * println(pts.beziers.length); // # of beziers * * beginShape(); * for (var i = 0; i < pts.beziers.length; i++) { @@ -593,16 +593,17 @@ pub.vertex = function() { * endShape(CLOSE); * * @example Isolated Paths of Points - * var pts = pathToPoints(obj); + * var pts = pathToPoints(outlines, 3); // add 3 for more detail * - * for (var i=0; i < pts.paths.length; i++) { - * var path = pts.paths[i]; - * println(path.points.length); # of points + * for (var j=0; j < pts.paths.length; j++) { + * var path = pts.paths[j]; * + * beginShape(); * for (var i = 0; i < path.points.length; i++) { * var pt = path.points[i]; - * point(pt.x, pt.y); + * vertex(pt.x + random(5), pt.y); * } + * endShape(CLOSE); * } */ From c0c75af0e1c307c1755e9d56ffb22b2ee95f5f28 Mon Sep 17 00:00:00 2001 From: ffd8 Date: Thu, 21 May 2020 15:25:47 +0200 Subject: [PATCH 9/9] review adjustments - cleaned up examples (captions, code, self-contained) - linted code - more typechecking and clearer error messages - removed debuging code --- basil.js | 165 +++++++++++++++++++++---------------- src/includes/shape.js | 137 +++++++++++++++++------------- src/includes/typography.js | 30 +++---- 3 files changed, 187 insertions(+), 145 deletions(-) diff --git a/basil.js b/basil.js index 2b00b9bb..5a86dacf 100644 --- a/basil.js +++ b/basil.js @@ -7736,7 +7736,7 @@ pub.vertex = function() { * @summary Get points and bezier coordinates from path(s). * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths (containing its array of points + beziers) of a given pageItem in InDesign. Together with `createOutlines()` this can be used on text items. Accepts both single paths or a collection/group of paths. * When using this on a multi path object (e.g. text with separate paths), the `paths` property can be used to loop over every path separately, whereas the properties `points` and `beziers` contain arrays for all paths combined. - * An optional second parameter allows to add and return additional points between existing points, which is helpful for subdividing existing paths. + * An optional second parameter adds interpolated points between existing points, which is helpful for subdividing existing paths. * * @cat Shape * @method pathToPoints @@ -7744,46 +7744,59 @@ pub.vertex = function() { * @param {Number} [addPoints] Optional amount of additional interpolated points. * @return {Object} Returns object with the following arrays `points`, `beziers`, `paths` * - * @example Points - * var pts = pathToPoints(obj); - * println(pts.points.length); // # of points + * @example Draw all points of a vector path + * noFill(); + * var myCircle = ellipse(width / 2, height / 2, width / 2, width / 2); + * var pts = pathToPoints(myCircle); * * for (var i = 0; i < pts.points.length; i++) { * var pt = pts.points[i]; - * point(pt.x, pt.y); + * ellipse(pt.x, pt.y, 3, 3); * } * - * @example Points w/ Interpolation - * var pts = pathToPoints(obj, 5); // adds 5 points between points - * println(pts.points.length); // # of points + * @example With Interpolation between Points + * noFill(); + * var myCircle = ellipse(width / 2, height / 2, width / 2, width / 2); + * var pts = pathToPoints(myCircle, 5); // add 5 points between each point * * for (var i = 0; i < pts.points.length; i++) { * var pt = pts.points[i]; - * point(pt.x, pt.y); + * ellipse(pt.x, pt.y, 3, 3); * } * - * @example Beziers - * var pts = pathToPoints(obj); - * println(pts.beziers.length); // # of beziers + * @example Draw Beziers and handles from Path + * noFill(); + * textSize(400); + * var myText = text('S', 0, 0, width, height); + * var myOutlines = createOutlines(myText); + * var pts = pathToPoints(myOutlines); * * beginShape(); * for (var i = 0; i < pts.beziers.length; i++) { * var bz = pts.beziers[i]; * vertex(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y, bz.right.x, bz.right.y); + * line(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y); // left handle + * line(bz.anchor.x, bz.anchor.y, bz.right.x, bz.right.y); // right handle * } * endShape(CLOSE); * - * @example Isolated Paths of Points - * var pts = pathToPoints(outlines, 3); // add 3 for more detail + * @example Separated Paths of Beziers + * noFill(); + * textSize(400); + * var myText = text('B', 0, 0, width, height); + * var myOutlines = createOutlines(myText); + * var pts = pathToPoints(myOutlines); // add 3 for more detail * - * for (var j=0; j < pts.paths.length; j++) { + * for (var j = 0; j < pts.paths.length; j++) { * var path = pts.paths[j]; * * beginShape(); - * for (var i = 0; i < path.points.length; i++) { - * var pt = path.points[i]; - * vertex(pt.x + random(5), pt.y); - * } + * for (var i = 0; i < path.beziers.length; i++) { + * var bz = path.beziers[i]; + * vertex(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y, bz.right.x, bz.right.y); + * line(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y); // left handle + * line(bz.anchor.x, bz.anchor.y, bz.right.x, bz.right.y); // right handle + * } * endShape(CLOSE); * } */ @@ -7792,47 +7805,55 @@ pub.pathToPoints = function(obj, addPoints) { var pz = {paths:[], points:[], beziers:[]}, pzGroup = false, grabPoints = function(formElm) { - for (var j=0; j < formElm.paths.length; j++) { - var paths = formElm.paths[j]; - var pzPaths = []; - var pzPoints = []; - var pzBeziers = []; - for (var i=0; i < paths.pathPoints.length; i++) { - var pt = paths.pathPoints[i]; - var pzBezier = { - anchor:{x:pt.anchor[0], y:pt.anchor[1]}, - left:{x:pt.leftDirection[0], y:pt.leftDirection[1]}, - right:{x:pt.rightDirection[0], y:pt.rightDirection[1]} - }; - pz.beziers.push(pzBezier) - pzBeziers.push(pzBezier) - - // optionally interpolated points - if (addPoints === undefined) { - var pzPoint = {x:pt.anchor[0], y:pt.anchor[1]}; - pz.points.push(pzPoint) - pzPoints.push(pzPoint); - } else { - var nextSel = (i + 1) % paths.pathPoints.length; - var nextPt = paths.pathPoints[nextSel]; - var amt = 1.0 / (addPoints + 1); - - if (formElm.paths[0].pathType === PathType.OPEN_PATH && i === paths.pathPoints.length-1) { - amt = 1; // don't interpolate end to start on open paths - } + if(formElm.hasOwnProperty('paths')){ + for (var j=0; j < formElm.paths.length; j++) { + var paths = formElm.paths[j]; + var pzPaths = []; + var pzPoints = []; + var pzBeziers = []; + for (var i=0; i < paths.pathPoints.length; i++) { + var pt = paths.pathPoints[i]; + var pzBezier = { + anchor:{x:pt.anchor[0], y:pt.anchor[1]}, + left:{x:pt.leftDirection[0], y:pt.leftDirection[1]}, + right:{x:pt.rightDirection[0], y:pt.rightDirection[1]} + }; + pz.beziers.push(pzBezier); + pzBeziers.push(pzBezier); + + // optionally interpolated points + if (addPoints === undefined) { + var pzPoint = {x:pt.anchor[0], y:pt.anchor[1]}; + pz.points.push(pzPoint); + pzPoints.push(pzPoint); + } else { + var nextSel = (i + 1) % paths.pathPoints.length; + var nextPt = paths.pathPoints[nextSel]; + var amt = 1.0 / (addPoints + 1); + + if (formElm.paths[0].pathType === PathType.OPEN_PATH && i === paths.pathPoints.length-1) { + amt = 1; // don't interpolate end to start on open paths + } - for (var t = 0; t < 1; t += amt) { - var ptStep = interpolateBezier(pt, nextPt, t); - var pzPointStep = {x:ptStep.x, y:ptStep.y}; - pz.points.push(pzPointStep) - pzPoints.push(pzPointStep); + for (var t = 0; t < 1; t += amt) { + var ptStep = interpolateBezier(pt, nextPt, t); + var pzPointStep = {x:ptStep.x, y:ptStep.y}; + pz.points.push(pzPointStep); + pzPoints.push(pzPointStep); + } } } - } - var pzPath = {points:pzPoints, beziers:pzBeziers}; - pz.paths.push(pzPath); + var pzPath = {points:pzPoints, beziers:pzBeziers}; + pz.paths.push(pzPath); + } + }else{ + error("pathToPoints(), no paths found. \nUse: polygon[s]"); } + }; + + if(obj === undefined){ + error("pathToPoints(), no paths found. \nUse: polygon[s]"); } // catch grouped items @@ -7842,7 +7863,7 @@ pub.pathToPoints = function(obj, addPoints) { } // process type as multi-line/character or single - if (obj instanceof Array || pzGroup) { + if (isArray(obj) || pzGroup) { for (var k=0; k < obj.length; k++) { grabPoints(obj[k]); } @@ -7851,7 +7872,7 @@ pub.pathToPoints = function(obj, addPoints) { } return pz; -} +}; // ---------------------------------------- // Shape Private @@ -9034,7 +9055,8 @@ pub.paragraphStyle = function(textOrName, props) { /** * @summary Convert text items to outlines. * @description Returns an array of polygons after outlining text and optionally processes them with a callback function. - * Use together with `pathToPoints()` for getting point and bezier coordinates from outlines. + * Use together with `pathToPoints()` for getting point and bezier coordinates from outlines. If used on a text frame, + * all linked text frames will be converted to outlines as well. * * @cat Typography * @method createOutlines @@ -9043,23 +9065,24 @@ pub.paragraphStyle = function(textOrName, props) { * @param {Function} [cb] Optional: Callback function to use with each polygon. Passed arguments: `obj`, `loopCounter` * @return {Array of Polygons} Returns an array of polygons. * - * @example + * @example Convert text to outlines * textSize(150); * var myText = text("Hello", 0, 0, width, height); * var outlines = createOutlines(myText); * - * @example w/ Callback + * @example Run a callback function on each resulting shape * textSize(150); * var myText = text("Hello \nWorld", 0, 0, width, height); * var outlines = createOutlines(myText, function(obj){ * var pts = pathToPoints(obj); + * println(pts.length); // check number of points * }); * */ pub.createOutlines = function(item, cb) { - var outlines, changedFill = false, debugCO = false; + var outlines, changedFill = false; // check if textFrames if (item.hasOwnProperty('createOutlines')) { @@ -9100,24 +9123,23 @@ pub.createOutlines = function(item, cb) { // error if neither } else { - error("Be sure to use:\n Story | TextFrame | Paragraph | Line | Word | Character | TextPath") - return false; + error("createOutlines(), incorrect first parameter. Use:\n Story | TextFrame | Paragraph | Line | Word | Character | TextPath"); } // remove tempFill for those items - if(changedFill){ - for(var i=0; i< outlines.length; i++){ + if (changedFill) { + for (var i=0; i < outlines.length; i++) { outlines[i].fillColor = "None"; } } // process outlines - if (outlines.length != undefined && outlines.length === 1) { + if (outlines.hasOwnProperty('length') && outlines.length === 1) { // multi-line text from group array if (outlines[0] instanceof Group) { outlines = ungroup(outlines[0]); - if (debugCO) println('group polygons'); // textframe multi-line + // textframe multi-line if (cb instanceof Function) { return forEach(outlines, cb); } else { @@ -9126,19 +9148,18 @@ pub.createOutlines = function(item, cb) { } // single polygon - if (debugCO) println('solo polygons/groups'); // single normal textFrame 1 line + // single normal textFrame 1 line if (cb instanceof Function) { return forEach(outlines, cb); } else { return outlines; } } else { - if (debugCO) println('array of polygons/groups'); // textPath, linked textFrames - + // textPath, linked textFrames // collection of polygons if (outlines[0] instanceof Group) { outlinesGroups = []; - for(var i=0; i < outlines.length; i++){ + for (var i=0; i < outlines.length; i++) { outlinesGroups.push(ungroup(outlines[i])); } outlines = outlinesGroups; @@ -9150,7 +9171,7 @@ pub.createOutlines = function(item, cb) { return outlines; } } -} +}; // ---------------------------------------- // Typography/Constants diff --git a/src/includes/shape.js b/src/includes/shape.js index 4f585c19..82f893ab 100644 --- a/src/includes/shape.js +++ b/src/includes/shape.js @@ -555,7 +555,7 @@ pub.vertex = function() { * @summary Get points and bezier coordinates from path(s). * @description Returns an object containing an array of all points, an array of all beziers (points + their anchor points) and an array of all paths (containing its array of points + beziers) of a given pageItem in InDesign. Together with `createOutlines()` this can be used on text items. Accepts both single paths or a collection/group of paths. * When using this on a multi path object (e.g. text with separate paths), the `paths` property can be used to loop over every path separately, whereas the properties `points` and `beziers` contain arrays for all paths combined. - * An optional second parameter allows to add and return additional points between existing points, which is helpful for subdividing existing paths. + * An optional second parameter adds interpolated points between existing points, which is helpful for subdividing existing paths. * * @cat Shape * @method pathToPoints @@ -563,46 +563,59 @@ pub.vertex = function() { * @param {Number} [addPoints] Optional amount of additional interpolated points. * @return {Object} Returns object with the following arrays `points`, `beziers`, `paths` * - * @example Points - * var pts = pathToPoints(obj); - * println(pts.points.length); // # of points + * @example Draw all points of a vector path + * noFill(); + * var myCircle = ellipse(width / 2, height / 2, width / 2, width / 2); + * var pts = pathToPoints(myCircle); * * for (var i = 0; i < pts.points.length; i++) { * var pt = pts.points[i]; - * point(pt.x, pt.y); + * ellipse(pt.x, pt.y, 3, 3); * } * - * @example Points w/ Interpolation - * var pts = pathToPoints(obj, 5); // adds 5 points between points - * println(pts.points.length); // # of points + * @example With Interpolation between Points + * noFill(); + * var myCircle = ellipse(width / 2, height / 2, width / 2, width / 2); + * var pts = pathToPoints(myCircle, 5); // add 5 points between each point * * for (var i = 0; i < pts.points.length; i++) { * var pt = pts.points[i]; - * point(pt.x, pt.y); + * ellipse(pt.x, pt.y, 3, 3); * } * - * @example Beziers - * var pts = pathToPoints(obj); - * println(pts.beziers.length); // # of beziers + * @example Draw Beziers and handles from Path + * noFill(); + * textSize(400); + * var myText = text('S', 0, 0, width, height); + * var myOutlines = createOutlines(myText); + * var pts = pathToPoints(myOutlines); * * beginShape(); * for (var i = 0; i < pts.beziers.length; i++) { * var bz = pts.beziers[i]; * vertex(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y, bz.right.x, bz.right.y); + * line(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y); // left handle + * line(bz.anchor.x, bz.anchor.y, bz.right.x, bz.right.y); // right handle * } * endShape(CLOSE); * - * @example Isolated Paths of Points - * var pts = pathToPoints(outlines, 3); // add 3 for more detail + * @example Separated Paths of Beziers + * noFill(); + * textSize(400); + * var myText = text('B', 0, 0, width, height); + * var myOutlines = createOutlines(myText); + * var pts = pathToPoints(myOutlines); // add 3 for more detail * - * for (var j=0; j < pts.paths.length; j++) { + * for (var j = 0; j < pts.paths.length; j++) { * var path = pts.paths[j]; * * beginShape(); - * for (var i = 0; i < path.points.length; i++) { - * var pt = path.points[i]; - * vertex(pt.x + random(5), pt.y); - * } + * for (var i = 0; i < path.beziers.length; i++) { + * var bz = path.beziers[i]; + * vertex(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y, bz.right.x, bz.right.y); + * line(bz.anchor.x, bz.anchor.y, bz.left.x, bz.left.y); // left handle + * line(bz.anchor.x, bz.anchor.y, bz.right.x, bz.right.y); // right handle + * } * endShape(CLOSE); * } */ @@ -611,47 +624,55 @@ pub.pathToPoints = function(obj, addPoints) { var pz = {paths:[], points:[], beziers:[]}, pzGroup = false, grabPoints = function(formElm) { - for (var j=0; j < formElm.paths.length; j++) { - var paths = formElm.paths[j]; - var pzPaths = []; - var pzPoints = []; - var pzBeziers = []; - for (var i=0; i < paths.pathPoints.length; i++) { - var pt = paths.pathPoints[i]; - var pzBezier = { - anchor:{x:pt.anchor[0], y:pt.anchor[1]}, - left:{x:pt.leftDirection[0], y:pt.leftDirection[1]}, - right:{x:pt.rightDirection[0], y:pt.rightDirection[1]} - }; - pz.beziers.push(pzBezier) - pzBeziers.push(pzBezier) - - // optionally interpolated points - if (addPoints === undefined) { - var pzPoint = {x:pt.anchor[0], y:pt.anchor[1]}; - pz.points.push(pzPoint) - pzPoints.push(pzPoint); - } else { - var nextSel = (i + 1) % paths.pathPoints.length; - var nextPt = paths.pathPoints[nextSel]; - var amt = 1.0 / (addPoints + 1); - - if (formElm.paths[0].pathType === PathType.OPEN_PATH && i === paths.pathPoints.length-1) { - amt = 1; // don't interpolate end to start on open paths - } - - for (var t = 0; t < 1; t += amt) { - var ptStep = interpolateBezier(pt, nextPt, t); - var pzPointStep = {x:ptStep.x, y:ptStep.y}; - pz.points.push(pzPointStep) - pzPoints.push(pzPointStep); + if(formElm.hasOwnProperty('paths')){ + for (var j=0; j < formElm.paths.length; j++) { + var paths = formElm.paths[j]; + var pzPaths = []; + var pzPoints = []; + var pzBeziers = []; + for (var i=0; i < paths.pathPoints.length; i++) { + var pt = paths.pathPoints[i]; + var pzBezier = { + anchor:{x:pt.anchor[0], y:pt.anchor[1]}, + left:{x:pt.leftDirection[0], y:pt.leftDirection[1]}, + right:{x:pt.rightDirection[0], y:pt.rightDirection[1]} + }; + pz.beziers.push(pzBezier); + pzBeziers.push(pzBezier); + + // optionally interpolated points + if (addPoints === undefined) { + var pzPoint = {x:pt.anchor[0], y:pt.anchor[1]}; + pz.points.push(pzPoint); + pzPoints.push(pzPoint); + } else { + var nextSel = (i + 1) % paths.pathPoints.length; + var nextPt = paths.pathPoints[nextSel]; + var amt = 1.0 / (addPoints + 1); + + if (formElm.paths[0].pathType === PathType.OPEN_PATH && i === paths.pathPoints.length-1) { + amt = 1; // don't interpolate end to start on open paths + } + + for (var t = 0; t < 1; t += amt) { + var ptStep = interpolateBezier(pt, nextPt, t); + var pzPointStep = {x:ptStep.x, y:ptStep.y}; + pz.points.push(pzPointStep); + pzPoints.push(pzPointStep); + } } } - } - var pzPath = {points:pzPoints, beziers:pzBeziers}; - pz.paths.push(pzPath); + var pzPath = {points:pzPoints, beziers:pzBeziers}; + pz.paths.push(pzPath); + } + }else{ + error("pathToPoints(), no paths found. \nUse: polygon[s]"); } + }; + + if(obj === undefined){ + error("pathToPoints(), no paths found. \nUse: polygon[s]"); } // catch grouped items @@ -661,7 +682,7 @@ pub.pathToPoints = function(obj, addPoints) { } // process type as multi-line/character or single - if (obj instanceof Array || pzGroup) { + if (isArray(obj) || pzGroup) { for (var k=0; k < obj.length; k++) { grabPoints(obj[k]); } @@ -670,7 +691,7 @@ pub.pathToPoints = function(obj, addPoints) { } return pz; -} +}; // ---------------------------------------- // Shape Private diff --git a/src/includes/typography.js b/src/includes/typography.js index 5f1b2c47..40000d44 100644 --- a/src/includes/typography.js +++ b/src/includes/typography.js @@ -486,7 +486,8 @@ pub.paragraphStyle = function(textOrName, props) { /** * @summary Convert text items to outlines. * @description Returns an array of polygons after outlining text and optionally processes them with a callback function. - * Use together with `pathToPoints()` for getting point and bezier coordinates from outlines. + * Use together with `pathToPoints()` for getting point and bezier coordinates from outlines. If used on a text frame, + * all linked text frames will be converted to outlines as well. * * @cat Typography * @method createOutlines @@ -495,23 +496,24 @@ pub.paragraphStyle = function(textOrName, props) { * @param {Function} [cb] Optional: Callback function to use with each polygon. Passed arguments: `obj`, `loopCounter` * @return {Array of Polygons} Returns an array of polygons. * - * @example + * @example Convert text to outlines * textSize(150); * var myText = text("Hello", 0, 0, width, height); * var outlines = createOutlines(myText); * - * @example w/ Callback + * @example Run a callback function on each resulting shape * textSize(150); * var myText = text("Hello \nWorld", 0, 0, width, height); * var outlines = createOutlines(myText, function(obj){ * var pts = pathToPoints(obj); + * println(pts.length); // check number of points * }); * */ pub.createOutlines = function(item, cb) { - var outlines, changedFill = false, debugCO = false; + var outlines, changedFill = false; // check if textFrames if (item.hasOwnProperty('createOutlines')) { @@ -552,24 +554,23 @@ pub.createOutlines = function(item, cb) { // error if neither } else { - error("Be sure to use:\n Story | TextFrame | Paragraph | Line | Word | Character | TextPath") - return false; + error("createOutlines(), incorrect first parameter. Use:\n Story | TextFrame | Paragraph | Line | Word | Character | TextPath"); } // remove tempFill for those items - if(changedFill){ - for(var i=0; i< outlines.length; i++){ + if (changedFill) { + for (var i=0; i < outlines.length; i++) { outlines[i].fillColor = "None"; } } // process outlines - if (outlines.length != undefined && outlines.length === 1) { + if (outlines.hasOwnProperty('length') && outlines.length === 1) { // multi-line text from group array if (outlines[0] instanceof Group) { outlines = ungroup(outlines[0]); - if (debugCO) println('group polygons'); // textframe multi-line + // textframe multi-line if (cb instanceof Function) { return forEach(outlines, cb); } else { @@ -578,19 +579,18 @@ pub.createOutlines = function(item, cb) { } // single polygon - if (debugCO) println('solo polygons/groups'); // single normal textFrame 1 line + // single normal textFrame 1 line if (cb instanceof Function) { return forEach(outlines, cb); } else { return outlines; } } else { - if (debugCO) println('array of polygons/groups'); // textPath, linked textFrames - + // textPath, linked textFrames // collection of polygons if (outlines[0] instanceof Group) { outlinesGroups = []; - for(var i=0; i < outlines.length; i++){ + for (var i=0; i < outlines.length; i++) { outlinesGroups.push(ungroup(outlines[i])); } outlines = outlinesGroups; @@ -602,7 +602,7 @@ pub.createOutlines = function(item, cb) { return outlines; } } -} +}; // ---------------------------------------- // Typography/Constants