Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Draw shape tool sucks

Why does roll20 not make actual rectangles and squares with the draw shape tool? There's always a chunk taken out of the top left corner it's really annoying when I'm trying to make maps.
1626060648

Edited 1626060674
The Aaron
Roll20 Production Team
API Scripter
It has to do with the way rectangles are constructed as SVG objects by Fabric JS, the drawing backend for Roll20, and how those objects are persisted.  It's something there might be a fix for in the future. In the interim, if you can get access to the API (Pro Subscriber Perk) in your game, here's a script that will fix it for all your rectangles by calling: !fix-rect-close And also fix any new rectangles you draw.  Here's the code: on('ready',()=>{ const pathType= (obj) => { try { let pd = JSON.parse(obj.get('path')); let key = pd.map(a=>a[0]).join(''); switch(key) { case 'MCCCC': return 'circle'; case 'MLLLLZ': return 'rectangle:closed'; case 'MLLLLL': // hack for fixing DL corners if( pd[0][1]===pd[4][1] && pd[0][2]===pd[4][2] && // begin and end same pd[1][1]===pd[2][1] && pd[0][2]===pd[1][2] && // top right corner pd[2][1]===pd[1][1] && pd[2][2]===pd[3][2] && // bottom right corner pd[3][1]===pd[4][1] && pd[3][2]===pd[2][2] && // bottom left corner pd[4][1]===pd[5][1] && pd[4][2]===pd[3][2] // top right corner (again, for DL fix) ) { return 'rectangle:dlclosed'; } else { return 'polyline'; } case 'MLLLL': if( pd[0][1]===pd[4][1] && pd[0][2]===pd[4][2] && // begin and end same pd[1][1]===pd[2][1] && pd[0][2]===pd[1][2] && // top right corner pd[2][1]===pd[1][1] && pd[2][2]===pd[3][2] && // bottom right corner pd[3][1]===pd[4][1] && pd[3][2]===pd[2][2] // bottom left corner ) { return 'rectangle'; } else { return 'polyline'; } default: if( /^MQ+L/.test(key)){ return 'pen'; } else if( /^ML+/.test(key)){ return 'polyline'; } else { return `custom:${key}`; } } } catch(e) { return 'invalid'; } }; const closeRectangle = (path) => { let fixedPath = JSON.parse(path.get('path')); if('walls'===path.get('layer')){ fixedPath.push([...fixedPath[1]]); } else { fixedPath.push(['Z']); } let props = JSON.parse(JSON.stringify(path)); delete props._type; delete props._id; props.pageid=props._pageid; delete props._pageid; delete props._path; createObj('path',{ ...props, path: JSON.stringify(fixedPath) }); path.remove(); }; on('add:path',(path)=>{ if('rectangle'===pathType(path)){ closeRectangle(path); } }); on('chat:message',msg=>{ if('api'===msg.type && /^!fix-rect-close(\b\s|$)/i.test(msg.content) && playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); let paths = findObjs({type:'path'}); let closedRects = 0; sendChat('',`/w "${who}" <div style="border:1px solid black;padding:.5em;background-color:white;">Considering <code>${paths.length}<code> path${1!==paths.length ? 's' : ''}.</div>`); const burndown = ()=>{ let p = paths.shift(); if(p){ if('rectangle'===pathType(p)){ ++closedRects; closeRectangle(p); } setTimeout(burndown,0); } else { sendChat('',`/w "${who}" <div style="border:1px solid black;padding:.5em;background-color:white;">Cloesed <code>${closedRects}<code> rectangle${1!==closedRects ? 's' : ''}.</div>`); } }; burndown(); } }); });