Celling 03. Preparing geometry for work#
Subject#
Importing initial shape (railing lines) for celling, checking of consistency
Workflow#
Importing DXF:
First of all we import DXF. Last 15 years using Blender I did not check for correctness of geometry. Reason for that was usage of blender, mostly for visualisation. But now checking for production, we found that Blender’s DXF importer works as shit in circles case. Deviation can achieve 200 mm, that doesn’t suit any needs of production ever in the world. So, the only way to avoid such disappointments was to write a short importer by myself for circles only to replace original bezier. On vertical it has deviations because of different rails levels. It required a little triangulation, that’s not calculated, but manually established and checked on viewer index node.
Код узла DXF импорта кругов.#''' in resol s d=128 n=2 in path FP d=[[]] n=0 out vers v d=[[]] n=0 out edges s d=[[]] n=0 ''' ''' Принцип работы - брать данные и в dxf ''' import bpy def make(): ''' ЗАГОТОВКА ДЛЯ БУДУЩИХ ОТДЕЛЬНЫХ УЗЛОВ DXF ИМПОРТА.''' def import_dxf(fp,resolution): import ezdxf from ezdxf import colors from ezdxf import units from ezdxf.tools.standards import setup_dimstyle from mathutils import Vector dxf = ezdxf.readfile(fp) lifehack = 50 ran = [i/lifehack for i in range(0,lifehack*360,int((lifehack*360)/resolution))] #print(ran) vers = [] edges = [] for a in dxf.query('Arc'): #a = dxf.query('Arc')[1] #arc = sverchok.utils.curve.primitives.SvCircle #arc.to_nurbs() vers_ = [] for i in a.vertices(ran): # line 43 is 35 in make 24 in import cen = a.dxf.center.xyz vers_.append([j/1000 for j,k in zip(i,cen)]) vers.append(vers_) edges.append([[i,i+1] for i in range(len(vers_)-1)]) edges[-1].append([len(vers_)-1,0]) return [vers], [edges] # MAKE DEFINITION BODY HERE # if self.inputs['path'].is_linked: fpath_ = self.inputs['path'].sv_get()[0][0] resol_ = self.inputs['resol'].sv_get()[0][0] verts_, edges_ = import_dxf(fpath_,resol_) if self.outputs['vers'].is_linked: self.outputs['vers'].sv_set(verts_) if self.outputs['edges'].is_linked: self.outputs['edges'].sv_set(edges_) make()
Surface:
Surface created from two lines with ordered vertices. I naturally reparametrized them only for better view, that is eye-checked quality. Separately created flat part of surface, it is the same as curved.
Raycasting on surface:
Raycasting pattern on surface. For cropping pattern I used other contour - offsetted from shape-definition curves. Dimensions are defined by further offset for plates. So, offset for plates is half of 14 mm, 7 mm. That means, I needed to offset the other 7 mm here.
Dissolve:
Dissolve accures with list of edges. Manually iterate all 2500 edges. So, i created edges generator, that choose edges by plate area threshold and boundary analysing. Output is flat list in texts, that i need to edit manually. So, need to finish at some stage and not return back for autogenerate list. That means, shape boundary and pattern need to be fixed by design. In practic it was several times changed. After that, i get text with text in node and dissolve it. Than check border elevations (heights) to manually esteblish correct surface shape.
Код узла нахождения лишних рёбер.#''' in vers v d=[[]] n=0 in edges s d=[[]] n=0 in pols s d=[[]] n=0 in areas s d=[[]] n=0 in border s d=[[]] n=0 in cutarea s d=0.1 n=2 ''' ''' Принцип работы - снаружи: определить площади определить границы снутри: определить граничащие полигоны определить граничащие рёбра далее: вывод индексов рёбер ''' import bpy self.make_operator('make') def ui(self, context, layout): cb_str = 'node.scriptlite_custom_callback' layout.operator(cb_str, text='B A K E').cb_name='make' def make(self, context): from mathutils import Vector as V def do_text(out_string): if not self.name in bpy.data.texts: bpy.data.texts.new(self.name) datablock = bpy.data.texts[self.name] datablock.clear() datablock.from_string(out_string) def main_border(vers,edges,pols,areas,border,cutarea): j = 0 # номер полигона короткого found = [] used = [] for pol,bor,ar in zip(pols,border,areas): if ar < cutarea and bor: fou = [i for i,po in enumerate(pols) if any([e in po for e in pol]) and po != pol] # i номер полигона большого for i in fou: #i = fou[0] if i not in used: used.append(i) # v это искомые индексы вершин, нужны индексы рёбер v = list(set(pols[i]) & set(pol)) if len(v) < 2: continue # записать индексы рёбер, в которых совпало два индекса вершин eds_ = [i for i,e in enumerate(edges) if len(set(v) & set(e))==2] a = lambda x: (V(vers[edges[x][0]])-V(vers[edges[x][1]])).length eds = sorted(eds_,key=a) #print(v,eds_) found.extend(eds) j += 1 foundout = sorted(list(set(found))) #print(foundout[:5],len(foundout)) #edges_out = [] #for i,e in enumerate(edges): # if i in foundout: edges_out.extend([True]) # else: edges_out.extend([False]) #print([edges_out]) return [foundout] if self.inputs['vers'].is_linked: vers = self.inputs['vers'].sv_get() else: return {'FINISHED'} if self.inputs['pols'].is_linked: pols = self.inputs['pols'].sv_get() else: return {'FINISHED'} if self.inputs['edges'].is_linked: edges = self.inputs['edges'].sv_get() else: return {'FINISHED'} if self.inputs['areas'].is_linked: areas = self.inputs['areas'].sv_get() else: return {'FINISHED'} if self.inputs['border'].is_linked: border = self.inputs['border'].sv_get() else: return {'FINISHED'} cutarea = self.inputs['cutarea'].sv_get() if type(cutarea[0][0]) == int: cutarea = cutarea[0] #print('\n'.join([str(i) for i in (vers[0][:5],edges[0][:5],pols[0][:5],areas[0][:5],border[0][:5],cutarea[0][0])])) edges_out = main_border(vers[0],edges[0],pols[0],areas[0],border[0],cutarea[0][0]) do_text(str(edges_out)) return {'FINISHED'}
Результат полуавтоматического списка индексов рёбер.#[[3, 7, 14, 21, 24, 30, 33, 37, 41, 43, 45, 48, 54, 57, 60, 63, 66, 70, 78, 82, 88, 95, 104, 106,108, 112, 115, 119, 126, 130, 134, 145, 149, 152, 156, 160, 164, 168, 172, 176, 180, 184, 188, 192, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 248, 252, 256, 260, 264, 268, 272, 276, 280, 284, 288, 292, 296, 301, 306, 310, 315, 321, 330, 333, 337, 341, 347, 351, 391, 400, 405, 415, 417, 423, 427, 529, 591, 599, 661, 684, 708, 710, 728, 730, 732, 752, 762, 765, 767, 771, 777, 788, 793, 795, 797, 799, 802,805, 808, 810, 814, 816, 818, 822, 826, 830, 834, 838, 842, 846, 850, 854, 858, 862, 866, 870, 874, 878, 882, 886, 890, 894, 898, 902, 906, 910, 914, 918, 922, 926, 930, 934, 938, 942, 946, 950, 957, 965, 969, 975, 981, 987, 996, 999, 1003, 1007, 1013, 1017, 1278, 1298, 1300, 1313, 1315, 1317, 1325, 1337, 1340, 1965, 1966]]
Separate corner plates:
To avoid jumping plates’ corners, happening far from original surface, i created part of triangulated plates. Defined by lines, snapped to projected pattern corners, i filtered plates by lines location, later that lines helped me to define left and right side of plates’ triangles, because, farthere offset happened only to two sides. Here I need to say, that all plates are completely flat. So, flattaning happens here in step forward. To flatten triangulated plate I separated triangles, flatted and joined pairs after.