def fullflattriareas(surfacemesh): ptsP = [P3(*p) for p in surfacemesh["pts"]] fptsP = [P2(*p) for p in surfacemesh["fpts"]] tris = surfacemesh["tris"] def P2Cross(a, b): return a.u * b.v - b.u * a.v triareas = [] ftriareas = [] cornerangs = [] fcornerangs = [] for tri in tris: p0, p1, p2 = ptsP[tri[0]], ptsP[tri[1]], ptsP[tri[2]] parea = 0.5 * P3.Cross(p1 - p0, p2 - p0).Len() triareas.append(parea) cornerangs.append(P3.Cross(P3.ZNorm(p1 - p0), P3.ZNorm(p2 - p0)).Len()) f0, f1, f2 = fptsP[tri[0]], fptsP[tri[1]], fptsP[tri[2]] farea = 0.5 * abs(P2Cross(f1 - f0, f2 - f0)) ftriareas.append(farea) fcornerangs.append(abs(P2Cross(P2.ZNorm(f1 - f0), P2.ZNorm(f2 - f0)))) surfacemesh["triareas"] = numpy.array(triareas) surfacemesh["ftriareas"] = numpy.array(ftriareas) surfacemesh["cornerangs"] = numpy.array(cornerangs) surfacemesh["fcornerangs"] = numpy.array(fcornerangs)
def tangentvec(self, n1, n2): vn = P2.ZNorm(self.nodes[n2] - self.nodes[n1]) if len(self.neighbournodes[n1] ) == 2 and not self.wingshape.uvonboundary(self.nodes[n1]): i = self.neighbournodes[n1].index(n2) nb = self.neighbournodes[n1][1 - i] vb = P2.ZNorm(self.nodes[n1] - self.nodes[nb]) vn = P2.ZNorm(vn + vb) return vn
def __init__(self, p, vp, r, zlo, zhi): self.p = p self.vp = vp self.vpf = P2(vp.x, vp.y) self.vpfsq = self.vpf.Lensq() self.vpflen = self.vpf.Len() self.vpfperpnorm = P2.APerp(self.vpf) * (1.0 / self.vpflen) self.r = r self.rsq = r * r self.zlo = zlo self.zhi = zhi self.lam = 2.0 self.Dllist = []
def isinnerpoly(self, poly, nodepoints): jbl = 0 ptbl = nodepoints[poly[jbl]] for j in range(1, len(poly)): pt = nodepoints[poly[j]] if pt.v < ptbl.v or (pt.v == ptbl.v and pt.u < ptbl.u): jbl = j ptbl = pt ptblFore = nodepoints[poly[(jbl + 1) % len(poly)]] ptblBack = nodepoints[poly[(jbl + len(poly) - 1) % len(poly)]] angFore = P2(ptblFore.u - ptbl.u, ptblFore.v - ptbl.v).Arg() angBack = P2(ptblBack.u - ptbl.u, ptblBack.v - ptbl.v).Arg() return (angBack < angFore)
def loadwingtrimlinesDeprecated(fname): lnodes, paths = json.load(open(fname)) nodes = {} for k, v in lnodes.items(): v = eval(v) p = P2(max(0, min(1, v[0] / 4)), max(0, min(1, v[1] / 4))) nodes[k] = p cr0 = max((k for k in nodes if nodes[k][1] <= 0), key=lambda x: nodes[x][0]) cr1 = min((k for k in nodes if nodes[k][0] >= 1), key=lambda x: nodes[x][1]) nodes["c4"] = P2(1, 0) paths.extend([cr0, "c4", "c4", cr1]) return nodes, paths
def CalcPixelYcuts(self, z, tbm): tbarpairs = [] barcuts = {} for bar in tbm.bars: # bucketing could speed this up assert bar.nodeback.p.z <= bar.nodefore.p.z if bar.nodeback.p.z <= z < bar.nodefore.p.z: bar1 = bar.barforeright node2 = bar1.GetNodeFore(bar1.nodeback == bar.nodefore) barC = bar1 if node2.p.z <= z else bar1.GetForeRightBL( bar1.nodeback == bar.nodefore) tbarpairs.append((bar.i, barC.i)) lam = (z - bar.nodeback.p.z) / (bar.nodefore.p.z - bar.nodeback.p.z) cx = Along(lam, bar.nodeback.p.x, bar.nodefore.p.x) cy = Along(lam, bar.nodeback.p.y, bar.nodefore.p.y) barcuts[bar.i] = P2(cx, cy) ycuts = [[] for iy in range(self.ypixels.nparts) ] # plural for set of all raster rows for i, i1 in tbarpairs: p0, p1 = barcuts[i], barcuts[i1] iyl, iyh = self.ypixmidsE.GetPartRange(min(p0.v, p1.v), max(p0.v, p1.v)) for iy in range(iyl, iyh): yc = self.ypixmidsE.vs[iy + 1] assert (p0.v < yc) != (p1.v < yc) lam = (yc - p0.v) / (p1.v - p0.v) xc = Along(lam, p0.u, p1.u) ycuts[iy].append(xc) return ycuts
def button_press_callback(event): global nodenamedown, cursor1, cursor2 if event.inaxes == axpara: mp = wingshape.clampuv(P2(event.xdata, event.ydata)) l, nn = parapolygraph.closestnodedist(mp) if cursor2 is not None: if l < nodeclickdistance: if nodenamedown == nn: parapolygraph.delnode(nodenamedown) nodenamedown = None cursor2 = None elif nodenamedown is not None: nodenamedown = parapolygraph.newnode(nodenamedown, mp) Dlineedits.append(("newnode", nodenamedown)) cursor2 = mp mupdater.set_data(parapolygraph.pointsdata()) lupdater.set_segments(parapolygraph.legsdata()) elif l < nodeclickdistance: if event.button == 1: nodenamedown = nn cursor1 = None elif event.button == 3: nodenamedown = nn cursor2 = parapolygraph.nodes[nodenamedown] cursorupdater.set_data(cursordata()) fig.canvas.draw_idle()
def surfacemesheslist(self, polysnodes, mesh_size): surfacemeshes = [] for i, polynodes in enumerate(polysnodes): polyloop = self.splinedpolypoints(polynodes, bclosed=True) with pygmsh.geo.Geometry() as g: g.add_polygon(polyloop, mesh_size=mesh_size) mesh = g.generate_mesh() uvpts = [P2(p[0], p[1]) for p in mesh.points] pts = numpy.array([self.wingshape.seval(p) for p in uvpts]) if "triangle" in mesh.cells_dict: surfacemesh = { "polynodes": polynodes, "polyloop": polyloop, "uvpts": uvpts, "pts": numpy.array(pts), "tris": mesh.cells_dict["triangle"], "linecontour": deriveclosedmeshcontour(len(pts), mesh.cells_dict["line"]) } surfacemeshes.append(surfacemesh) else: print("Polygon %d untriangulatable" % i) return surfacemeshes
def DistLamPedgePZF(self, p0z, p1z): self.Dllist.append([p0z, p1z]) assert self.zlo <= p0z.z <= p1z.z <= self.zhi v = p1z - p0z vf = P2(v.x, v.y) vfsq = vf.Lensq() if vfsq == 0.0: return vflen = vf.Len() vfperpnorm = P2.CPerp(vf) * (1.0 / vflen) lv = self.p - p0z dp = P2.DotLZ(vfperpnorm, lv) dpm = P2.DotLZ(vfperpnorm, self.vp) if dpm > 0.0: laml = (-self.r - dp) / dpm elif dpm < 0.0: laml = (self.r - dp) / dpm else: return if 0 < laml < self.lam: lvp = lv + self.vp * laml assert abs(abs(P2.DotLZ(vfperpnorm, lvp)) - self.r) < 0.001 mu = P2.DotLZ(vf, lvp) / vfsq if 0 < mu < 1: assert abs(P2.DotLZ(vf, lvp - v * mu)) < 0.001 self.lam = laml
def DistLamPpointPZ(self, p0): assert self.zlo <= p0.z <= self.zhi, (self.zlo, p0.z, self.zhi) lv = self.p - p0 sd = P2.DotLZ(self.vpfperpnorm, lv) sasq = self.rsq - sd * sd lvf = P2(p0.x - self.p.x, p0.y - self.p.y) if sasq < 0.0: return sa = math.sqrt(sasq / self.vpfsq) lamc = P2.Dot(self.vpf, lvf) / self.vpfsq if lamc + sa < 0.0: return laml = lamc - sa if laml < 0.0: self.lam = 0.0 # shouldn't happen elif laml < self.lam: self.lam = laml
def shoulddivide(self, rd, contourdelta, contourdotdiff, b2dcontournormals): if self.vc.Len() <= contourdelta: return False if b2dcontournormals: nd = P2.Dot( P2.ZNorm( P2(self.bar.nodemid.pointzone.v.x, self.bar.nodemid.pointzone.v.y)), P2.ZNorm( P2(self.cbar.nodemid.pointzone.v.x, self.cbar.nodemid.pointzone.v.y))) else: nd = P3.Dot(self.bar.nodemid.pointzone.v, self.cbar.nodemid.pointzone.v) / (rd * rd) if nd >= contourdotdiff: return False return True
def sevalconvO(self, p): uniformchordlength = self.sectionchordlengths[self.Isect] vsecl = p.v*(self.nchorddivs-1) vsec = max(0, min(self.nchorddivs-2, int(math.floor(vsecl)))) vr = vsecl - vsec v = uniformchordlength[vsec]*(1-vr) + uniformchordlength[vsec+1]*vr usecl = p.u*(self.nsections-1) usec = max(0, min(self.nsections-2, int(math.floor(usecl)))) ur = usecl - usec u = self.leadingedgelengths[usec]*(1-ur) + self.leadingedgelengths[usec+1]*ur return P2(u, v+self.sectionchordranges[self.Isect][0])
def motion_notify_callback(event): global cursor1 if event.inaxes == axpara: mp = wingshape.clampuv(P2(event.xdata, event.ydata)) if nodenamedown is not None and cursor2 is None: parapolygraph.nodes[nodenamedown] = mp mupdater.set_data(parapolygraph.pointsdata()) lupdater.set_segments(parapolygraph.legsdata()) fig.canvas.draw_idle() else: cursor1 = mp cursorupdater.set_data(cursordata()) fig.canvas.draw_idle()
def DistPpointPZ(self, p0): if not (self.zlo <= p0.z <= self.zhi): return lv = p0 - self.p lvf = P2(lv.x, lv.y) lvflen = lvf.Len() if lvflen < self.r: self.r = lvflen self.v = lv assert self.zlo <= self.p.z + self.v.z <= self.zhi, (self.zlo, self.p.z + self.v.z, self.zhi) self.Dpp = [p0]
def deriveflatpathstretchratios(self, surfacemeshes): nodepairreallengths = {} nodepairflatlengths = {} for surfacemesh in surfacemeshes: polynodes = surfacemesh["polynodes"] uvpts = surfacemesh["uvpts"] pts = surfacemesh["pts"] fpts = surfacemesh["fpts"] linecontour = surfacemesh["linecontour"] for i in range(len(polynodes)): n1, n2 = polynodes[i], polynodes[(i + 1) % len(polynodes)] p1, p2 = self.nodes[n1], self.nodes[n2] i1, i2 = uvpts.index(p1), uvpts.index(p2) j1, j2 = linecontour.index(i1), linecontour.index(i2) if j2 == 0: j2 = len(linecontour) - 1 assert (j1 < j2) lenreal = sum((P3(*pts[linecontour[j + 1]]) - P3(*pts[linecontour[j]])).Len() for j in range(j1, j2)) lenflat = sum((P2(*fpts[linecontour[j + 1]]) - P2(*fpts[linecontour[j]])).Len() for j in range(j1, j2)) nodepairflatlengths[(n1, n2)] = lenflat nodepairreallengths[(n1, n2)] = lenreal self.flatpathratios = {} self.flatpathtable = [] for i in range(0, len(self.paths), 2): n1, n2 = self.paths[i], self.paths[i + 1] lenga = nodepairflatlengths.get((n1, n2), -1) lengb = nodepairflatlengths.get((n2, n1), -1) if lenga != -1 and lengb != -1: self.flatpathratios[(n1, n2)] = lenga / lengb lengreal = nodepairreallengths.get( (n1, n2)) or nodepairreallengths.get((n2, n1)) or -1 self.flatpathtable.append((n1, n2, lenga, lengb, lengreal))
def loadwinggeometry(fname, fac=1.0): r = csv.reader(open(fname)) k = list(r) wingmeshuvudivisions = eval(k[0][-3]) assert (wingmeshuvudivisions == len(k[0])/3-1), 'Section numbering incorrect' sections = [] zvals = [] for i in range(0, (wingmeshuvudivisions*3)+2, 3): pts = [ ] z = float(k[2][i+1]) for j in range(2, len(k)): assert (z == float(k[j][i+1])) pts.append(P2(float(k[j][i]), float(k[j][i+2]))*fac) zvals.append(z*fac) sections.append(pts) assert(len(sections) == wingmeshuvudivisions+1) return sections, zvals
def snap_nodes(self, wingshape, tol=0.1): vs = [0] for ni, pti in self.nodes.items(): for l in wingshape.leadingedgelengths: nptu, nptv = pti.u, pti.v if 0 < abs(pti.u - l) < tol: nptu = l break else: nptu = pti.u for v in vs: if 0 < abs(pti.v - v) < tol: nptv = v break if nptv == pti.v: vs.append(pti.v) if nptu != pti.u or nptv != pti.v: npt = P2(nptu, nptv) print("Snapping point", ni, 'from', pti, 'to', npt) self.nodes[ni] = npt
def __init__(self, wingshape, trimfile, deprecatedTrimFile=False, splineweight=0.21, legsampleleng=0.05): self.legsampleleng = legsampleleng self.splineweight = splineweight self.wingshape = wingshape if deprecatedTrimFile: snodes, self.paths = loadwingtrimlinesDeprecated(trimfile) self.nodes = dict( (n, self.wingshape.sevalconvO(p)) for n, p in snodes.items()) else: jdata = json.load(open(trimfile)) self.nodes = dict((nn, P2(*p)) for nn, p in jdata["nodes"].items()) self.paths = jdata["paths"] self.flatpathlengths = {} self.Inodemax = max( int(re.sub("[^\d]", "", nn) or "0") for nn in self.nodes)
def DistPedgePZF(self, p0z, p1z): assert self.zlo <= p0z.z <= p1z.z <= self.zhi v = p1z - p0z vf = P2(v.x, v.y) vfsq = vf.Lensq() if vfsq == 0.0: return vflen = vf.Len() lv = self.p - p0z dp = abs(P2.DotLZ(P2.CPerp(vf), lv) / vflen) if dp >= self.r: return lam = P2.DotLZ(vf, lv) / vfsq if 0 < lam < 1: self.r = dp self.v = v * lam - lv assert abs(P2.DotLZ(vf, self.v)) < 0.001 assert abs(P2(self.v.x, self.v.y).Len() - self.r) < 0.001 assert self.zlo <= self.p.z + self.v.z <= self.zhi, (self.zlo, self.p.z, self.p.z + self.v.z, self.zhi) self.Dpp = [p0z, p1z]
def cursordata(): return zip(*[cursor1 or P2(0, 0), cursor2 or P2(0, 0.1)])
(ocont, conts, dpts) = pickle.load(open(geofile, "rb")) xmin, ymin = min(p[0] for p in ocont), min(p[1] for p in ocont) def ThinTooshort(cont): res = [cont[0]] for p in cont[1:]: if (res[-1] - p).Len() < 1e-5: continue else: res.append(p) if res[-1] != res[0]: res[-1] = res[0] return res conts = [ ThinTooshort([P2(p[0] - xmin, p[1] - ymin) for p in cont]) for cont in conts ] ocont = ThinTooshort([P2(p[0] - xmin, p[1] - ymin) for p in ocont]) dpts = [P2(p[0] - xmin, p[1] - ymin) for p in dpts] sendactivity(contours=conts) sendactivity(contours=[ocont]) sendactivity(points=dpts) from vor2d import DFullBuildVB conts.append(ocont) vbs, vbm = DFullBuildVB(conts)
def linesegmentnetflipyz(self, flipyz): seglines = [ [flipyz(self.seval(P2(u,v))) for v in numpy.linspace(self.vrange[0], self.vrange[1], 51)] for u in self.leadingedgelengths ] spanlines = [ [flipyz(self.seval(P2(u,v))) for u in numpy.linspace(self.urange[0], self.urange[1], 51)] for v in numpy.linspace(self.vrange[0], self.vrange[1], 21) ] return seglines+spanlines
def clampuv(self, p): return P2(max(self.urange[0], min(self.urange[1], p.u)), max(self.vrange[0], min(self.vrange[1], p.v)))
def sevalconv(self, p): return P2(self.leadingedgelengthconv(p[0]), self.sectionchordlengthconv(self.Isect, p[1]))