Skip to content

Instantly share code, notes, and snippets.

@pratu16x7
Last active March 23, 2025 15:43
Show Gist options
  • Save pratu16x7/f0bd5a8eba79e7df7812bc40d69e0e92 to your computer and use it in GitHub Desktop.
Save pratu16x7/f0bd5a8eba79e7df7812bc40d69e0e92 to your computer and use it in GitHub Desktop.
// ======================================
// Show cut/score layers (Set _STR var to select "cut"/"score") and hide the rest (if want to show back the rest, set hideNode HIDENODERESET TO 1)
// ======================================
// https://medium.com/@jebsphone/reusable-function-for-iterating-figma-objects-d2277cb65786
const iterateNodes = async (hasStr:(node:any) => boolean,
hideNode:(node:any, nodeNum:number) => void, showNode:(node:any, nodeNum:number) => void):Promise<any[]> => {
const nodesFound = [] as any[]; // eventually, the return value
// Check if there's a selection, and if not, ask user if they want
// to examine all objects on the page.
let nodes = figma.currentPage.selection;
if (nodes.length === 0){
// "confirm" here is a Scripter function. Change if writing a plugin.
nodes = figma.currentPage.children;
const confirmed = await confirm(`No items selected. Act upon whole page, on ${nodes.length} children?`)
if (confirmed){
nodes = figma.currentPage.children;
} else {
return nodesFound;
}
}
for (const node of nodes){
runFor(node, nodesFound);
// If current node has children, test and change its descendants
if ("children" in node && !node.removed){
node.findAll().forEach(node => {
runFor(node, nodesFound);
});
}
}
return nodesFound;
}
let _STR = "cut"
// _STR = "score"
// ~1500 items for 6 corr-like pieces, ~300-ish per piece: 20-30 sec
const HIDENODERESET = 0 /// 0
const runFor = (node:any, nodesFound: any[]):void => {
if (hasStr(node)){
// Change the node with hideNode and pass it the zero-based running
// count of nodes we've changed thus far.
showNode(node, nodesFound.length);
nodesFound.push(node);
} else if (toHide(node)) {
hideNode(node, nodesFound.length);
nodesFound.push(node);
// } else if (isFrame(node)) {
} else {
if(node.fills) {
const fills = clone(node.fills)
if (fills && fills.length) {
// fills[0].color.r = 0; // https://www.figma.com/plugin-docs/editing-properties/#changing-colors
fills[0].visible = false;
node.fills = fills;
}
}
}
}
const hasStr = (node:any):boolean => {
return (node.name.toLowerCase().includes(_STR));
}
const hideNode = (node:any, nodeNum:number):void => {
// node.name = '*_' + node.name;
if ('opacity' in node) {
const opacityNode = node as MinimalBlendMixin;
opacityNode.opacity = HIDENODERESET; /// 0
}
// node.visible = false;
}
const showNode = (node:any, nodeNum:number):void => {
// node.name = '==' + node.name;
if ('opacity' in node) {
const opacityNode = node as MinimalBlendMixin;
opacityNode.opacity = 1;
}
// node.visible = true;
}
const toHide = (node:any): boolean => {
return !isParent(node) || isGuide(node);
}
const isGuide = (node:any): boolean => {
const nameL = node.name.toLowerCase();
return nameL.includes("[guide]");
}
const isFrame = (node:any): boolean => {
const nameL = node.name.toLowerCase();
return nameL.includes("frame");
}
const isParent = (node:any): boolean => {
const nameL = node.name.toLowerCase();
return "children" in node && !nameL.includes("guide") && node.opacity;
// return "children" in node ||
// nameL.includes("frame") ||
// nameL.includes("group") ||
// nameL.includes("piece") ||
// nameL.includes("sheet") ||
// nameL.includes("limit") ||
// nameL === "l"
}
function clone(val) {
return JSON.parse(JSON.stringify(val));
}
const changedNodes = await iterateNodes(hasStr, hideNode, showNode);
// provide "toast" status to the user
figma.notify(`${changedNodes.length} items hidden.`)
// ======================================
// Process Current Children Nodes
// ======================================
// https://medium.com/@jebsphone/reusable-function-for-iterating-figma-objects-d2277cb65786
const MODE = "cut"
// const MODE = "draw"
const DRAW_STR = "draw"
const isNodeToChange = (node:any):boolean => {
return (node.name.includes(DRAW_STR));
}
const changeNode = (node:any, nodeNum:number):void => {
if(MODE === "cut") {
node.visible = false;
} else {
node.visible = true;
}
}
const iterateNodes = async (isNodeToChange:(node:any) => boolean,
changeNode:(node:any, nodeNum:number) => void):Promise<any[]> => {
const nodesFound = [] as any[]; // eventually, the return value
// Check if there's a selection, and if not, ask user if they want
// to examine all objects on the page.
let nodes = figma.currentPage.selection;
if (nodes.length === 0){
// "confirm" here is a Scripter function. Change if writing a plugin.
const confirmed = await confirm("No items selected. Act upon whole page?")
if (confirmed){
nodes = figma.currentPage.children;
} else {
return nodesFound;
}
}
for (const node of nodes){
// Test each node using the isNodeToChange function
if (isNodeToChange(node)){
// Change the node with changeNode and pass it the zero-based running
// count of nodes we've changed thus far.
changeNode(node, nodesFound.length);
nodesFound.push(node);
}
// If current node has children, test and change its descendants
if ("children" in node && !node.removed){
node.findAll(node => isNodeToChange(node)).forEach(node => {
changeNode(node, nodesFound.length);
nodesFound.push(node);
});
}
}
return nodesFound;
}
const changedNodes = await iterateNodes(isNodeToChange, changeNode);
// provide "toast" status to the user
figma.notify(`${changedNodes.length} items hidden.`)
// ---
// // test if the current node is a text node named #calloutLetter
// const isNodeToChange2 = (node:any):boolean => {
// return (node.type === 'TEXT' && node.name === '#calloutLetter');
// }
// // here we will use the nodeNum parameter to calculate which letter to use
// const changeNode2 = (node:TextNode, nodeNum:number):void => {
// node.characters = String.fromCharCode('A'.charCodeAt(0) + nodeNum));
// }
// // here we call iterateNodes and give the user a status message
// const changedNodes2 = await iterateNodes(isNodeToChange, changeNode);
// figma.notify(`${changedNodes.length} callouts updated.`);
// const changeNode3 = (node:TextNode, nodeNum:number):void => {
// const fontName = node.fontName as FontName;
// figma.loadFontAsync(fontName).then(() => {
// node.characters = String.fromCharCode('A'.charCodeAt(0) + nodeNum));
// });
// }
// let $our_element = selection()[0]
// selection() is of type readonly Scene node
// print($our_element.children)
// print(figma.currentPage.selection)
// let childs = figma.currentPage.findChildren(n => n.parent === selection()[0])
// print($our_element.name, $our_element.type)
// print(childs)
// print($our_element.children)
// ======================================
// Make a Building Roll-out for selection
// ======================================
const STICKER_WIDTH = 30
const STROKE_WIDTH = 2
const OUTPUT_FRAME = "Foundation Print 1"
const SIDES = ["back", "left", "front", "right"]; // anticlockwise unrolling
const LABEL_X = 40;
const LABEL_Y = 40;
let root = figma.root.children[0];
print(root.type)
let $our_element = selection()[0]
let height = 400
let foundation_height = 400
let facade_name = "CorridorPanel";
let x_unroll = get_x_unroll($our_element, height, color_obj(1, 1, 1), false, facade_name)
let x_unroll_foundation = get_x_unroll($our_element, foundation_height, color_obj(0.8, 0.8, 0.8), true)
x_unroll_foundation.y = height;
add_to_paper(figma.group([x_unroll, x_unroll_foundation], root));
// scripts don't need things to be components anyway. working with just groups is fine.
// [enhance] front and left side marker (they're opp to main L)
// [enhance lvl 2] tiny map on each to show which building instead of same
// ==== HELPERS =============================================
function get_x_unroll($element, height, color, put_name=false, facade_name="", show_sides=false) {
let el_width = $element.width
let el_height = $element.height
let name = put_name? $element.name : "";
let widths = [el_width, el_height, el_width, el_height]
let x_global = 0;
let sticker = rect(x_global, 0, STICKER_WIDTH, height, sticker_fill())
x_global += STICKER_WIDTH
let unroll = widths.map(function(w) {
let face = rect(x_global, 0, w, height, color, name)
if(facade_name.length) {
print(facade_name);
let facade = get_facade(facade_name, w);
facade.x += x_global;
// facade.y = 0;
face = figma.group([face, facade], root)
}
x_global += w;
return face
})
unroll.push(sticker)
return figma.group(unroll, root)
}
function get_facade(facade_name, total_width) {
let panel_compnent = figma.currentPage.findOne(n => n.type === "COMPONENT" && n.name === facade_name);
let instance_count = Math.floor(total_width / panel_compnent.width)
let panel_lain_x = (total_width - panel_compnent.width * instance_count)/ 2;
print(total_width, panel_lain_x);
let panels = range(0, instance_count).map(function(i) {
let panel = panel_compnent.createInstance();
panel.x = panel_lain_x;
panel_lain_x += panel.width;
return panel;
})
let facade = figma.group(panels, root);
return facade;
}
function add_to_paper(printable) {
let frames = figma.currentPage.findChildren(n => n.type === "FRAME" && n.name === OUTPUT_FRAME);
let paper_page_frame = frames[0];
print(paper_page_frame.type);
print(paper_page_frame.id);
paper_page_frame.appendChild(printable);
}
function color_obj(r, g, b) {
return {
type:"SOLID",
visible:true,
opacity:1,
blendMode:"NORMAL",
color:{r:r, g:g, b:b}
}
}
function rect(x, y, w, h, color, label="fooo") {
let $rect = Rectangle({
x: x,
y: y,
width: w,
height: h,
fills: [color],
strokes: [ BLACK.paint ],
strokeWeight: STROKE_WIDTH,
strokeAlign: "INSIDE"
})
if(!label) {
return $rect;
}
let $label = Text({
x:x + LABEL_X,
y:y + LABEL_Y,
width:279.6812744140625,
height:62.5,
rotation:0,
layoutAlign:"INHERIT",
constrainProportions:false,
layoutGrow:0,
layoutPositioning:"AUTO",
characters:label,
fontSize:32,
fills: [BLACK.paint],
textCase:"UPPER",
textDecoration:"NONE",
})
if($label.width > $rect.width) {
return $rect;
}
let g = figma.group([$rect, $label], root);
return g;
}
function sticker_fill() {
return {
"type": "IMAGE",
"visible": true,
"opacity": 1,
"blendMode": "NORMAL",
"scaleMode": "TILE",
"imageTransform": [[1,0,0], [0,1,0]],
"scalingFactor": 0.03999999910593033,
"rotation": 0,
"filters": {
"exposure": 0,
"contrast": 0,
"saturation": 0,
"temperature": 0,
"tint": 0,
"highlights": 0,
"shadows": 0
},
"imageHash": "772da98746c9965bc12da9dbc51373531a4da552"
}
}
// viewport.scrollAndZoomIntoView(setSelection(rectangles))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment