Hi Mark, thanks for giving that a try. No, I could not get it to work either. I also tried this one (see attached), but I am not sure this works either (it is actually still running on my machine after quite a bit of time). Are you aware of any method to "convert" a mesh into a simplified line drawing?
Hi @chadulus2, I've written a script that works with the idea I mentioned earlier—saving as an Illustrator version 8 file. It works because it reads the plain text coordinates directly from the saved file and then draws them into the document. Each patch of the mesh is a path item.
Give it a try and let me know if it helps in your case.
- Mark

/**
* @file Draw Geometry Of MeshItems.js
*
* Will add a path item for each patch
* of each mesh item in the document.
*
* Note: document must be saved before
* running script.
*
* @author m1b
* @version 2025-10-18
* @discussion https://community.adobe.com/t5/illustrator-discussions/greyed-out-script/m-p/15545562
*/
(function () {
var settings = {
drawAsPatches: false,
layerName: 'meshes',
showUI: true,
};
if (0 === app.documents.length)
return alert('Please open a document and try again.');
var doc = app.activeDocument;
if (!doc.saved)
return alert('Please save document and try again.');
if (settings.showUI) {
var result = ui(settings);
if (2 === result)
// user cancelled
return;
}
// how to draw the mesh geometry
settings.drawMesh = settings.drawAsPatches ? drawMeshAsPatches : drawMeshAsGridLines;
// save temp document as AI version 8 (plain text)
var original = doc.fullName;
var so = new IllustratorSaveOptions();
so.compatibility = Compatibility.ILLUSTRATOR8;
so.compressed = true;
var tempFile = File(Folder.temp + '/tmp.ai');
doc.saveAs(tempFile, so);
doc.close();
// restore original document
app.open(original);
doc = app.activeDocument;
// read the mesh coords
var meshes = readMeshData(tempFile);
// draw the meshes as path items
var pathItems = [];
// get a safe layer to draw to
var layer = getThing(doc, 'name', settings.layerName) || doc.layers.add();
layer.name = settings.layerName;
layer.visible = true;
layer.locked = false;
// draw the meshes
for (var i = 0; i < meshes.length; i++) {
var group = layer.groupItems.add();
group.name = 'mesh ' + (i + 1);
pathItems = pathItems.concat(settings.drawMesh(group, meshes[i]));
}
if (0 === pathItems.length)
alert('No mesh items found in document.');
})();
/**
* Extract MeshItem coordinates from an Illustrator v8 file.
* Will return an array containing mesh data for each mesh found.
* @author m1b
* @version 2025-10-18
* @param {File} file - the file to read.
* @returns {Array<Array<patch>>} - the mesh data objects.
*/
function readMeshData(file) {
const MATCH_PATCH_START = /%_\[([^\]]+)\] \/P X#/;
const MATCH_PATCH_END = /%_\/E X#/;
const MATCH_PATCH_COORDS = /%_\[([^\]]+)\] \/N X#/;
if (!file.exists)
return;
file.open('r');
var meshes = [];
var mesh;
var patch;
var line;
var match;
var coords;
while (!file.eof) {
line = file.readln();
match = line.match(MATCH_PATCH_START);
if (
null !== match
&& 2 === match.length
) {
// start new patch
patch = { name: match[1], rowIndex: -1, columnIndex: -1, points: [] };
var parts = patch.name.split(' ');
if (2 !== parts.length)
continue;
patch.rowIndex = Number(parts[0]);
patch.columnIndex = Number(parts[1]);
if (
isNaN(patch.rowIndex)
|| isNaN(patch.columnIndex)
)
continue;
if (
0 === patch.rowIndex
&& 0 === patch.columnIndex
) {
// start new mesh
mesh = [];
meshes.push(mesh);
}
mesh.push(patch);
continue;
}
if (!patch)
// nothing
continue;
var match = line.match(MATCH_PATCH_COORDS);
if (
null === match
|| 2 > match.length
) {
if (MATCH_PATCH_END.test(line))
// end of patch
patch = undefined;
// part of a patch, but not geometry;
continue;
}
var coords = match[1].split(' ');
// start new point
var numbers = [];
for (var i = 0; i < coords.length; i++) {
numbers.push(Number(coords[i]));
}
var len = numbers.length;
patch.points.push({
anchor: [numbers[len - 8], numbers[len - 7]],
leftDirection: [numbers[len - 3], numbers[len - 2]],
rightDirection: [numbers[len - 6], numbers[len - 5]],
});
}
return meshes;
};
/**
* Draws mesh patches.
* @author m1b
* @version 2025-10-18
* @param {Document|Layer|GroupItem} container - the location of the new path items.
* @param {Object} patches - patch objects { name:"", points:[] }.
* @returns {Array<PathItem>}
*/
function drawMeshAsPatches(container, patches) {
if (!container.hasOwnProperty('pathItems'))
throw new Error('drawMesh: bad `container` supplied.');
var pathItems = [];
for (var i = 0; i < patches.length; i++) {
var patch = patches[i];
var pathItem = drawPath(container, patch.points, patch.name);
pathItem.closed = true;
pathItems.push(pathItem);
}
return pathItems;
};
/**
* Draw mesh grid lines (rows and columns).
* @author m1b
* @version 2025-10-18
* @param {Document|Layer|GroupItem} [container] - the container to draw in.
* @param {Array<Object>} patches - array of patches {name: points:}.
* @returns {Array<PathItem>}
*/
function drawMeshAsGridLines(container, patches) {
var map = [];
var maxRow = 0;
var maxCol = 0;
// parse patch names
for (var i = 0; i < patches.length; i++) {
var p = patches[i];
if (!map[p.rowIndex])
map[p.rowIndex] = [];
map[p.rowIndex][p.columnIndex] = p;
if (p.rowIndex > maxRow)
maxRow = p.rowIndex;
if (p.columnIndex > maxCol)
maxCol = p.columnIndex;
}
var rowCount = maxRow + 1;
var columnCount = maxCol + 1;
// draw the lines
var pathItems = [];
var linePts = [];
/* ------------------ *
* HORIZONTAL LINES *
* ------------------ */
for (var r = 0; r < rowCount; r++) {
for (var c = 0; c < columnCount; c++) {
var patch = map[r][c];
if (!linePts[c]) {
linePts[c] = [];
}
if (!linePts[c + 1]) {
linePts[c + 1] = [];
}
if (0 === c) {
// right side of patch
if (0 === r)
linePts[c][r] = {
anchor: patch.points[0].anchor,
leftDirection: patch.points[0].leftDirection,
rightDirection: patch.points[0].rightDirection,
pointType: PointType.CORNER,
};
else
// fix join
linePts[c][r].rightDirection = patch.points[0].rightDirection;
linePts[c][r + 1] = {
anchor: patch.points[1].anchor,
leftDirection: patch.points[1].leftDirection,
rightDirection: patch.points[1].rightDirection,
pointType: PointType.CORNER,
};
}
// left side of patch
if (0 === r)
linePts[c + 1][r] = {
anchor: patch.points[3].anchor,
leftDirection: patch.points[3].rightDirection,
rightDirection: patch.points[3].leftDirection,
pointType: PointType.CORNER,
};
else
// fix join
linePts[c + 1][r].rightDirection = patch.points[3].leftDirection;
linePts[c + 1][r + 1] = {
anchor: patch.points[2].anchor,
leftDirection: patch.points[2].rightDirection,
rightDirection: patch.points[2].leftDirection,
pointType: PointType.CORNER,
};
}
}
// draw horizontal lines
for (var i = 0; i < linePts.length; i++)
pathItems.push(drawPath(container, linePts[i], 'row ' + (i + 1)));
/* ----------------- *
* VERTICAL LINES *
* ----------------- */
var linePts = [];
for (var c = 0; c < columnCount; c++) {
for (var r = 0; r < rowCount; r++) {
var patch = map[r][c];
if (!linePts[r]) {
linePts[r] = [];
}
if (!linePts[r + 1]) {
linePts[r + 1] = [];
}
if (0 === r) {
// bottom edge of patch
if (0 === c)
linePts[r][c] = {
anchor: patch.points[0].anchor,
leftDirection: patch.points[0].rightDirection,
rightDirection: patch.points[0].leftDirection,
pointType: PointType.CORNER,
};
else
// fix join
linePts[r][c].rightDirection = patch.points[0].leftDirection;
linePts[r][c + 1] = {
anchor: patch.points[3].anchor,
leftDirection: patch.points[3].rightDirection,
rightDirection: patch.points[3].leftDirection,
pointType: PointType.CORNER,
};
}
// top edge of patch
if (0 === c)
linePts[r + 1][c] = {
anchor: patch.points[1].anchor,
leftDirection: patch.points[1].leftDirection,
rightDirection: patch.points[1].rightDirection,
pointType: PointType.CORNER,
};
else
// fix join
linePts[r + 1][c].rightDirection = patch.points[1].rightDirection;
linePts[r + 1][c + 1] = {
anchor: patch.points[2].anchor,
leftDirection: patch.points[2].leftDirection,
rightDirection: patch.points[2].rightDirection,
pointType: PointType.CORNER,
};
}
}
// draw vertical lines
for (var i = 0; i < linePts.length; i++)
pathItems.push(drawPath(container, linePts[i], 'column ' + (i + 1)));
return pathItems;
};
/**
* Draws a path of `points`;
* @author m1b
* @version 2025-10-18
* @param {Document|Layer|GroupItem} [container] - the container to draw in.
* @param {Array<PathPoints>} points - an array of points {anchor: leftDirection: rightDirection: pointType?:}.
* @param {String} [name] - the path item name (default: none).
* @returns {PathItem}
*/
function drawPath(container, points, name) {
var path = newEmptyPathItem(container, name);
for (var i = 0; i < points.length; i++) {
var p = path.pathPoints.add();
p.anchor = points[i].anchor;
p.leftDirection = points[i].leftDirection;
p.rightDirection = points[i].rightDirection;
p.pointType = points.pointType || PointType.CORNER;
}
return path;
};
/**
* Returns a new empty path item.
* @author m1b
* @version 2025-10-16
* @param {Document|Layer|GroupItem} container - the location of the new path items.
* @param {String} [name] - the name of the item (default: none).
* @returns {PathItem}
*/
function newEmptyPathItem(container, name) {
var item = container.pathItems.add();
item.name = name || '';
item.filled = false;
item.stroked = true;
return item;
};
/**
* Returns a thing with matching property.
* If `key` is undefined, evaluate the object itself.
* @author m1b
* @version 2024-04-21
* @param {Array|Collection} things - the things to look through.
* @param {String} [key] - the property name (default: undefined).
* @param {*} value - the value to match.
*/
function getThing(things, key, value) {
for (var i = 0, obj; i < things.length; i++)
if ((undefined == key ? things[i] : things[i][key]) == value)
return things[i];
};
/**
* User interface for this script. Will update the `settings` object.
* @author m1b
* @version 2025-10-18
* @param {Object} settings - the script settings.
* @returns {1|2}
*/
function ui(settings) {
var w = new Window("dialog { text:'Draw MeshItem Geometry' }");
var group = w.add('group {orientation:"column", alignment:["fill","fill"], alignChildren:["left","fill"], margins:[10,10,10,10] }');
var radioButtons = group.add('group {orientation:"row", margins:[10,10,10,10] }'),
asPatchesRadio = radioButtons.add('radiobutton {text:"Draw mesh as patches"}'),
asLinesRadio = radioButtons.add('radiobutton {text:"Draw mesh as lines"}');
var bottomUI = w.add('group {orientation:"row", alignment:["fill","top"], margins: [0,20,0,0] }'),
buttons = bottomUI.add('group {orientation:"row", alignment:["right","top"], alignChildren:"right" }'),
cancelButton = buttons.add('button', undefined, 'Cancel', { name: 'cancel' }),
drawButton = buttons.add('button', undefined, 'Draw', { name: 'ok' });
(settings.drawAsPatches ? asPatchesRadio : asLinesRadio).value = true;
drawButton.onClick = function () {
settings.drawAsPatches = asPatchesRadio.value;
w.close(1);
};
w.center();
return w.show();
};
Edit 2025-10-18: added capability to draw geometry as grid lines, as well as patches.
