I’m trying to figure out how to create a face with holes from imported data. The data has the potential to be complicated and I need to make it versatile so it can work with any file.
Here is my sample xml file.
<?xml version="1.0" encoding="UTF-8"?>
<DATA_EXPORT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LOCATION address="14685 U.S. 224, Van Wert OH 45891" lat="40.92415722243234" long="-84.54056337475778"/>
<STRUCTURES>
<ROOF id="ROOF1">
<FACES>
<FACE id="F1" type="ROOF" name="RF-1">
<POLYGON id="P1" path="L1,L2,L3,L4" size="3.2060012921337275" pitch="17"/>
</FACE>
<FACE id="F2" type="ROOF" name="RF-2">
<POLYGON id="P2" path="L5,L6,L7,L8" size="2.4168373719967726" pitch="16"/>
</FACE>
<FACE id="F3" type="ROOF" name="RF-3">
<POLYGON id="P3" path="L9,L10,L11,L12" size="2.7098196305321696" pitch="16"/>
</FACE>
<FACE id="F4" type="ROOF" name="RF-4">
<POLYGON id="P4" path="L13,L14,L15" size="25.257929282695628" pitch="10"/>
</FACE>
<FACE id="F5" type="ROOF" name="RF-5">
<POLYGON id="P5" path="L16,L14,L17,L18,L19,L20,L21,L22,L23,L24,L25,L26,L27,L28,L29,L30,L31,L32,L33,L34,L35,L36,L37,L38,L39" size="839.817469986963" pitch="12"/>
</FACE>
<FACE id="F6" type="ROOF" name="RF-6">
<POLYGON id="P6" path="L17,L13,L40" size="25.276322988876863" pitch="10"/>
</FACE>
<FACE id="F7" type="ROOF" name="RF-7">
<POLYGON id="P7" path="L41,L42,L43,L44,L45" size="379.6890197075424" pitch="12"/>
</FACE>
<FACE id="F8" type="ROOF" name="RF-8">
<POLYGON id="P8" path="L46,L47,L48,L49,L50,L51,L52,L53,L54" size="372.5245083256959" pitch="12"/>
</FACE>
<FACE id="F9" type="ROOF" name="RF-9">
<POLYGON id="P9" path="L55,L56,L57,L58" size="49.769822247225825" pitch="12"/>
</FACE>
<FACE id="F10" type="ROOF" name="RF-10">
<POLYGON id="P10" path="L59,L60,L61,L33" size="37.367293074030044" pitch="12"/>
</FACE>
<FACE id="F11" type="ROOF" name="RF-11">
<POLYGON id="P11" path="L23,L62,L63,L64" size="37.3672930740305" pitch="12"/>
</FACE>
<FACE id="F12" type="ROOF" name="RF-12">
<POLYGON id="P12" path="L57,L65,L66,L67" size="49.7698222472263" pitch="12"/>
</FACE>
<FACE id="F13" type="ROOF" name="RF-13">
<POLYGON id="P13" path="L32,L68,L69,L59" size="37.367293074030506" pitch="12"/>
</FACE>
<FACE id="F14" type="ROOF" name="RF-14">
<POLYGON id="P14" path="L64,L70,L71,L24" size="37.36729307402987" pitch="12"/>
</FACE>
<FACE id="F15" type="ROOF" name="RF-15">
<POLYGON id="P15" path="L45,L72,L73,L74" size="379.68901970754484" pitch="12"/>
</FACE>
<FACE id="F16" type="ROOF" name="RF-16">
<POLYGON id="P16" path="L75,L76,L47,L77,L78,L79,L80,L81" size="372.5245083256973" pitch="12"/>
</FACE>
<FACE id="F17" type="ROOF" name="RF-17">
<POLYGON id="P17" path="L82,L83,L20,L84" size="1004.6592049315932" pitch="12"/>
</FACE>
<FACE id="F18" type="ROOF" name="RF-18">
<POLYGON id="P18" path="L3,L85,L86" size="1.4174791631200416" pitch="14"/>
</FACE>
<FACE id="F19" type="ROOF" name="RF-19">
<POLYGON id="P19" path="L1,L87,L88" size="1.4174791631200478" pitch="14"/>
</FACE>
</FACES>
<LINES>
<LINE id="L1" type="HIP" path="C1,C2"/>
<LINE id="L2" type="FLASHING" path="C3,C2"/>
<LINE id="L3" type="HIP" path="C4,C3"/>
<LINE id="L4" type="EAVE" path="C4,C1"/>
<LINE id="L5" type="EAVE" path="C5,C6"/>
<LINE id="L6" type="RAKE" path="C6,C7"/>
<LINE id="L7" type="FLASHING" path="C7,C8"/>
<LINE id="L8" type="OTHER" path="C8,C5"/>
<LINE id="L9" type="FLASHING" path="C9,C10"/>
<LINE id="L10" type="RAKE" path="C9,C11"/>
<LINE id="L11" type="EAVE" path="C11,C12"/>
<LINE id="L12" type="OTHER" path="C12,C10"/>
<LINE id="L13" type="RIDGE" path="C13,C14"/>
<LINE id="L14" type="VALLEY" path="C13,C15"/>
<LINE id="L15" type="RAKE" path="C15,C14"/>
<LINE id="L16" type="EAVE" path="C16,C15"/>
<LINE id="L17" type="VALLEY" path="C17,C13"/>
<LINE id="L18" type="EAVE" path="C17,C18"/>
<LINE id="L19" type="RAKE" path="C18,C19"/>
<LINE id="L20" type="RIDGE" path="C19,C20"/>
<LINE id="L21" type="RAKE" path="C20,C16"/>
<LINE id="L22" type="OTHER" path="C21,C22"/>
<LINE id="L23" type="VALLEY" path="C23,C21"/>
<LINE id="L24" type="VALLEY" path="C24,C23"/>
<LINE id="L25" type="OTHER" path="C25,C24"/>
<LINE id="L26" type="OTHER" path="C26,C25"/>
<LINE id="L27" type="STEPFLASH" path="C27,C26"/>
<LINE id="L28" type="FLASHING" path="C28,C27"/>
<LINE id="L29" type="STEPFLASH" path="C29,C28"/>
<LINE id="L30" type="OTHER" path="C22,C29"/>
<LINE id="L31" type="OTHER" path="C30,C31"/>
<LINE id="L32" type="VALLEY" path="C32,C30"/>
<LINE id="L33" type="VALLEY" path="C33,C32"/>
<LINE id="L34" type="OTHER" path="C34,C33"/>
<LINE id="L35" type="OTHER" path="C35,C34"/>
<LINE id="L36" type="STEPFLASH" path="C36,C35"/>
<LINE id="L37" type="FLASHING" path="C37,C36"/>
<LINE id="L38" type="STEPFLASH" path="C38,C37"/>
<LINE id="L39" type="OTHER" path="C31,C38"/>
<LINE id="L40" type="RAKE" path="C14,C17"/>
<LINE id="L41" type="STEPFLASH" path="C39,C40"/>
<LINE id="L42" type="RAKE" path="C40,C41"/>
<LINE id="L43" type="EAVE" path="C42,C41"/>
<LINE id="L44" type="RAKE" path="C42,C43"/>
<LINE id="L45" type="RIDGE" path="C39,C43"/>
<LINE id="L46" type="STEPFLASH" path="C44,C45"/>
<LINE id="L47" type="RIDGE" path="C46,C44"/>
<LINE id="L48" type="RAKE" path="C46,C47"/>
<LINE id="L49" type="EAVE" path="C47,C48"/>
<LINE id="L50" type="RAKE" path="C45,C48"/>
<LINE id="L51" type="STEPFLASH" path="C49,C50"/>
<LINE id="L52" type="OTHER" path="C51,C49"/>
<LINE id="L53" type="STEPFLASH" path="C52,C51"/>
<LINE id="L54" type="FLASHING" path="C50,C52"/>
<LINE id="L55" type="EAVE" path="C53,C54"/>
<LINE id="L56" type="RAKE" path="C55,C54"/>
<LINE id="L57" type="RIDGE" path="C55,C56"/>
<LINE id="L58" type="STEPFLASH" path="C56,C53"/>
<LINE id="L59" type="RIDGE" path="C32,C57"/>
<LINE id="L60" type="RAKE" path="C58,C57"/>
<LINE id="L61" type="EAVE" path="C58,C33"/>
<LINE id="L62" type="EAVE" path="C21,C59"/>
<LINE id="L63" type="RAKE" path="C59,C60"/>
<LINE id="L64" type="RIDGE" path="C60,C23"/>
<LINE id="L65" type="RAKE" path="C61,C55"/>
<LINE id="L66" type="EAVE" path="C61,C62"/>
<LINE id="L67" type="STEPFLASH" path="C62,C56"/>
<LINE id="L68" type="EAVE" path="C30,C63"/>
<LINE id="L69" type="RAKE" path="C57,C63"/>
<LINE id="L70" type="RAKE" path="C60,C64"/>
<LINE id="L71" type="EAVE" path="C64,C24"/>
<LINE id="L72" type="RAKE" path="C43,C65"/>
<LINE id="L73" type="EAVE" path="C65,C66"/>
<LINE id="L74" type="STEPFLASH" path="C66,C39"/>
<LINE id="L75" type="EAVE" path="C67,C68"/>
<LINE id="L76" type="RAKE" path="C67,C46"/>
<LINE id="L77" type="STEPFLASH" path="C68,C44"/>
<LINE id="L78" type="STEPFLASH" path="C69,C70"/>
<LINE id="L79" type="FLASHING" path="C70,C71"/>
<LINE id="L80" type="STEPFLASH" path="C71,C72"/>
<LINE id="L81" type="OTHER" path="C72,C69"/>
<LINE id="L82" type="EAVE" path="C73,C74"/>
<LINE id="L83" type="RAKE" path="C74,C20"/>
<LINE id="L84" type="RAKE" path="C19,C73"/>
<LINE id="L85" type="STEPFLASH" path="C3,C75"/>
<LINE id="L86" type="EAVE" path="C75,C4"/>
<LINE id="L87" type="EAVE" path="C1,C76"/>
<LINE id="L88" type="STEPFLASH" path="C2,C76"/>
</LINES>
<POINTS>
<POINT id="C1" data="31.091046303012742,7.586463308561623,7.758104672798754"/>
<POINT id="C2" data="30.221308746663713,7.51370426827543,8.582104672798756"/>
<POINT id="C3" data="28.45270027352784,5.695735388858146,8.582104672798756"/>
<POINT id="C4" data="28.40390384869119,4.824324920829124,7.758104672798754"/>
<POINT id="C5" data="15.706111382244515,-8.672111176948265,18.029010148648826"/>
<POINT id="C6" data="17.343249712044486,-6.98928166431988,18.029010148648826"/>
<POINT id="C7" data="16.710733927027086,-6.3739395704923245,19.21276014864884"/>
<POINT id="C8" data="16.065029879329494,-7.037664686513466,19.21276014864884"/>
<POINT id="C9" data="21.809337847117888,-1.1330378129850585,19.21276014864884"/>
<POINT id="C10" data="22.59280014105867,-0.32770973149299126,19.21276014864884"/>
<POINT id="C11" data="22.44185363213529,-1.748379906812615,18.029010148648826"/>
<POINT id="C12" data="24.21797236736531,0.07730883993011746,18.029010148648826"/>
<POINT id="C13" data="15.904557771599487,-0.35532857100302423,23.74251014864882"/>
<POINT id="C14" data="19.959420308921487,-4.3000959001377295,23.74251014864882"/>
<POINT id="C15" data="15.174153343099285,-9.218915726972282,18.029010148648826"/>
<POINT id="C16" data="3.2756088790540474,-21.449538770207692,18.029010148648826"/>
<POINT id="C17" data="24.75058616092231,0.6247874459317752,18.029010148648826"/>
<POINT id="C18" data="36.25088968152554,12.446054968889902,18.029010148648826"/>
<POINT id="C19" data="25.536758123962212,22.869282819232847,33.125746496847455"/>
<POINT id="C20" data="-7.438522678509282,-11.026310919864741,33.125746496847455"/>
<POINT id="C21" data="20.104878969694997,9.347384283146331,27.5350532824926"/>
<POINT id="C22" data="20.422855826596905,9.038040944134906,27.08700831051463"/>
<POINT id="C23" data="19.986199513150613,14.046802404181621,30.930568110436145"/>
<POINT id="C24" data="24.687103804133873,14.057495239887752,27.535053282492658"/>
<POINT id="C25" data="25.005080661035972,13.748151900876442,27.08700831051461"/>
<POINT id="C26" data="24.512783887964705,13.242115530206888,27.08700831051461"/>
<POINT id="C27" data="27.93126830656536,9.916447760823587,22.27019626179496"/>
<POINT id="C28" data="24.333637018268714,6.218409545421025,22.27019626179496"/>
<POINT id="C29" data="20.91515259966815,9.54407731480444,27.08700831051461"/>
<POINT id="C30" data="1.4678647515131094,-9.809773656868307,27.5350532824926"/>
<POINT id="C31" data="1.7858416084150026,-10.11911699587973,27.08700831051461"/>
<POINT id="C32" data="1.3491852949686953,-5.110355535833015,30.930568110436184"/>
<POINT id="C33" data="6.0500895859519845,-5.099662700126866,27.535053282492658"/>
<POINT id="C34" data="6.368066442853987,-5.409006039138272,27.08700831051463"/>
<POINT id="C35" data="5.875769669782812,-5.915042409807735,27.08700831051461"/>
<POINT id="C36" data="9.294254088383463,-9.240710179191035,22.27019626179496"/>
<POINT id="C37" data="5.696622800086819,-12.938748394593583,22.27019626179496"/>
<POINT id="C38" data="2.278138381486258,-9.613080625210179,27.08700831051461"/>
<POINT id="C39" data="25.13722879373017,21.842429548962002,24.03248635912418"/>
<POINT id="C40" data="34.91087454931145,12.334151991267959,10.12515254738877"/>
<POINT id="C41" data="35.0641903987084,12.184998882334428,9.906992942517084"/>
<POINT id="C42" data="48.44789168912583,25.942228256145388,9.906992942517084"/>
<POINT id="C43" data="38.520930084147594,35.59965892277296,24.03248635912418"/>
<POINT id="C44" data="-6.630124919055305,-10.397224739383327,23.858861466259214"/>
<POINT id="C45" data="3.350592435137266,-20.10695161119713,9.802180264781887"/>
<POINT id="C46" data="-19.91902115613677,-24.057003128878574,23.858861466259174"/>
<POINT id="C47" data="-9.826086351608735,-33.87590058977346,9.644135019271951"/>
<POINT id="C48" data="3.4628098854727636,-20.216122200278217,9.644135019271951"/>
<POINT id="C49" data="-10.058225830431228,-20.78440938624603,19.027535291943526"/>
<POINT id="C50" data="-8.035106837977992,-22.752597858956193,16.17820716247564"/>
<POINT id="C51" data="-8.531060654899486,-19.21462228807394,19.027535291943526"/>
<POINT id="C52" data="-6.5079416624462505,-21.182810760784104,16.17820716247564"/>
<POINT id="C53" data="2.1442177246180543,18.994747002316462,8.17466371481018"/>
<POINT id="C54" data="-1.4786954315423817,22.51929298215448,8.17466371481018"/>
<POINT id="C55" data="-6.371897659348913,17.489525509043347,15.082226892908787"/>
<POINT id="C56" data="-2.74898450318841,13.964979529205257,15.082226892908787"/>
<POINT id="C57" data="8.222683509418124,-11.797228627992942,30.930568110436145"/>
<POINT id="C58" data="10.513795926637593,-9.44217314962224,27.53505328249264"/>
<POINT id="C59" data="24.568585310380573,5.004873833651004,27.535053282492317"/>
<POINT id="C60" data="26.85969772760002,7.359929312021677,30.930568110436145"/>
<POINT id="C61" data="-11.265099887155436,12.459758035932225,8.17466371481018"/>
<POINT id="C62" data="-7.6421867309949745,8.93521205609421,8.174663714809904"/>
<POINT id="C63" data="5.931571092198681,-14.152284106363616,27.535053282492317"/>
<POINT id="C64" data="29.150810144819488,9.714984790392378,27.53505328249264"/>
<POINT id="C65" data="28.593968479169344,45.25708958940055,9.906992942517084"/>
<POINT id="C66" data="15.210267188751928,31.499860215589575,9.906992942516771"/>
<POINT id="C67" data="-30.011955960664782,-14.238105667983712,9.64413501927161"/>
<POINT id="C68" data="-16.723059723583287,-0.5783272784885108,9.644135019271895"/>
<POINT id="C69" data="-16.919034623148093,-14.109881180211204,19.027535291943526"/>
<POINT id="C70" data="-18.942153615601327,-12.141692707501042,16.17820716247564"/>
<POINT id="C71" data="-17.414988440069585,-10.571905609328951,16.17820716247564"/>
<POINT id="C72" data="-15.39186944761635,-12.540094082039124,19.027535291943526"/>
<POINT id="C73" data="14.822626566398881,33.29251066957581,18.029010148648826"/>
<POINT id="C74" data="-18.152654236072603,-0.6030830695218015,18.029010148648485"/>
<POINT id="C75" data="26.21242026088466,3.3929310056220174,7.758104672798756"/>
<POINT id="C76" data="32.46158875930689,9.816508651511553,7.758104672798756"/>
</POINTS>
</ROOF>
</STRUCTURES>
</DATA_EXPORT>
Here is the code I have so far.
require 'rexml/document'
include REXML
def import_eagleview(file_path)
xml = File.read(file_path)
doc = REXML::Document.new(xml)
roof_data = doc.root.elements['STRUCTURES'].elements[1].elements.to_a
points = roof_data[2].elements.to_a.map { |pnt| [pnt.attributes['id'], pnt.attributes['data'].split(',').map(&:to_f)] }.to_h
edges = {}
entities = Sketchup.active_model.entities
roof_data[1].elements.each do |l|
pnts = l.attributes['path'].split(',')
pnt1 = points[pnts[0]]
pnt2 = points[pnts[1]]
edge = entities.add_edges(pnt1, pnt2)
edges[l.attributes['id']] = edge
end
faces = {}
roof_data[0].elements.each do |f|
polygon = f.elements[1]
lines = polygon.attributes['path'].split(',').map { |l| edges[l] }.flatten
loops = loops_from_edges(lines)
pm = Geom::PolygonMesh.new
a = true
if a
points = []
loops.each { |l| mesh_points_from_edges(l).each { |pt| points.push(pt) } }
points.push(points[0])
points.each { |pnt| pm.add_point(pnt) }
pm.add_polygon((1..(pm.points.length)).to_a)
entities.add_faces_from_mesh(pm)
else
pts = lines.map { |l| [l.start, l.end] }.flatten.uniq
pts.each { |pt| pm.add_point(pt) }
pm.add_point(pts[0])
face = entities.add_face(loops[0])
end
1..(loops.length - 1).each { |i| entities.add_face(loops[i]) } if loops.length > 1
faces[f.attributes['id']] = face
end
end
def mesh_points_from_edges(edges)
pnts = edges.map { |l| [l.start, l.end] }.flatten.uniq
pnts.push pnts[0]
end
def loops_from_edges(edges)
loops = []
current_loop = []
last = nil
edges.each do |edge|
unless last.nil? || [edge.start, edge.end, last.start, last.end].uniq.length < 4
loops.push current_loop.to_a
current_loop = []
end
current_loop.push edge
last = edge
end
loops.push(current_loop)
loops
end
I looked at this topic, and I think I’m close, but apparently I’m missing some small detail.