mirror of
https://github.com/NGSolve/netgen.git
synced 2024-11-14 10:08:32 +05:00
Unified webgui.Draw()
This commit is contained in:
parent
993ed0bde3
commit
d632ed3d75
414
python/webgui.py
414
python/webgui.py
@ -3,248 +3,240 @@ import numpy as np
|
|||||||
from time import time
|
from time import time
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import webgui_jupyter_widgets
|
||||||
from webgui_jupyter_widgets import BaseWebGuiScene, encodeData, WebGuiDocuWidget
|
from webgui_jupyter_widgets import BaseWebGuiScene, encodeData, WebGuiDocuWidget
|
||||||
import webgui_jupyter_widgets.widget as wg
|
import webgui_jupyter_widgets.widget as wg
|
||||||
|
|
||||||
|
from packaging.version import parse
|
||||||
|
|
||||||
|
if parse(webgui_jupyter_widgets.__version__) >= parse("0.2.18"):
|
||||||
|
_default_width = None
|
||||||
|
_default_height = None
|
||||||
|
else:
|
||||||
|
_default_width = "100%"
|
||||||
|
_default_height = "50vh"
|
||||||
|
|
||||||
|
|
||||||
|
_registered_draw_types = {}
|
||||||
|
|
||||||
|
|
||||||
|
def register_draw_type(*types):
|
||||||
|
def inner(func):
|
||||||
|
for typ in types:
|
||||||
|
_registered_draw_types[typ] = func
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
class WebGLScene(BaseWebGuiScene):
|
class WebGLScene(BaseWebGuiScene):
|
||||||
def __init__(self, mesh, clipping, on_init):
|
def __init__(self, obj, args=[], kwargs={}):
|
||||||
from IPython.display import display, Javascript
|
self.obj = obj
|
||||||
import threading
|
self.args = args
|
||||||
self.mesh = mesh
|
self.kwargs = kwargs
|
||||||
self.clipping = clipping
|
self.encoding = "b64"
|
||||||
self.on_init = on_init
|
|
||||||
|
|
||||||
def GetData(self, set_minmax=True):
|
def GetData(self, set_minmax=True):
|
||||||
import json
|
self.kwargs["encoding"] = self.encoding
|
||||||
# d = BuildRenderData(self.mesh, self.cf, self.order, draw_surf=self.draw_surf, draw_vol=self.draw_vol, deformation=self.deformation, region=self.region)
|
typ = type(self.obj)
|
||||||
d = self.mesh._webgui_data()
|
d = None
|
||||||
bp = d['Bezier_trig_points']
|
if type(self.obj) in _registered_draw_types:
|
||||||
for i in range(len(bp)):
|
d = _registered_draw_types[typ](self.obj, self.args, self.kwargs)
|
||||||
bp[i] = encodeData(np.array(bp[i], dtype=np.float32))
|
else:
|
||||||
|
import inspect
|
||||||
|
|
||||||
ep = d['edges']
|
for t in inspect.getmro(typ):
|
||||||
for i in range(len(ep)):
|
if t in _registered_draw_types:
|
||||||
ep[i] = encodeData(np.array(ep[i], dtype=np.float32))
|
print("check type", t)
|
||||||
|
d = _registered_draw_types[t](self.obj, self.args, self.kwargs)
|
||||||
|
break
|
||||||
|
if d is None and hasattr(self.obj, "_webgui_data"):
|
||||||
|
d = self.obj._webgui_data()
|
||||||
|
bp = d["Bezier_trig_points"]
|
||||||
|
for i in range(len(bp)):
|
||||||
|
bp[i] = encodeData(np.array(bp[i], dtype=np.float32))
|
||||||
|
|
||||||
if self.clipping is not None:
|
ep = d["edges"]
|
||||||
d['clipping'] = True
|
for i in range(len(ep)):
|
||||||
if isinstance(self.clipping, dict):
|
ep[i] = encodeData(np.array(ep[i], dtype=np.float32))
|
||||||
|
|
||||||
|
if d is None:
|
||||||
|
raise RuntimeError(f"Cannot draw object of type {typ}")
|
||||||
|
|
||||||
|
args = self.args
|
||||||
|
kwargs = self.kwargs
|
||||||
|
if "clipping" in kwargs:
|
||||||
|
clipping = kwargs["clipping"]
|
||||||
|
d["clipping"] = True
|
||||||
|
if isinstance(clipping, dict):
|
||||||
allowed_args = ("x", "y", "z", "dist", "function", "pnt", "vec")
|
allowed_args = ("x", "y", "z", "dist", "function", "pnt", "vec")
|
||||||
if "vec" in self.clipping:
|
if "vec" in clipping:
|
||||||
vec = self.clipping["vec"]
|
vec = clipping["vec"]
|
||||||
self.clipping["x"] = vec[0]
|
clipping["x"] = vec[0]
|
||||||
self.clipping["y"] = vec[1]
|
clipping["y"] = vec[1]
|
||||||
self.clipping["z"] = vec[2]
|
clipping["z"] = vec[2]
|
||||||
if "pnt" in self.clipping:
|
if "pnt" in clipping:
|
||||||
d['mesh_center'] = list(self.clipping["pnt"])
|
d["mesh_center"] = list(clipping["pnt"])
|
||||||
for name, val in self.clipping.items():
|
for name, val in clipping.items():
|
||||||
if not (name in allowed_args):
|
if not (name in allowed_args):
|
||||||
raise Exception('Only {} allowed as arguments for clipping!'.format(", ".join(allowed_args)))
|
raise Exception(
|
||||||
d['clipping_' + name] = val
|
"Only {} allowed as arguments for clipping!".format(
|
||||||
|
", ".join(allowed_args)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
d["clipping_" + name] = val
|
||||||
|
|
||||||
if self.on_init:
|
if "on_init" in kwargs:
|
||||||
d['on_init'] = self.on_init
|
d["on_init"] = kwargs["on_init"]
|
||||||
|
|
||||||
|
if set_minmax:
|
||||||
|
if "min" in kwargs:
|
||||||
|
d["funcmin"] = kwargs["min"]
|
||||||
|
if "max" in kwargs:
|
||||||
|
d["funcmax"] = kwargs["max"]
|
||||||
|
d["autoscale"] = kwargs["autoscale"]
|
||||||
|
|
||||||
|
if "vectors" in kwargs:
|
||||||
|
d["vectors"] = True
|
||||||
|
if isinstance(kwargs["vectors"], dict):
|
||||||
|
for name, val in kwargs["vectors"].items():
|
||||||
|
if not (name in ("grid_size", "offset")):
|
||||||
|
raise Exception(
|
||||||
|
'Only "grid_size" and "offset" allowed as arguments for vectors!'
|
||||||
|
)
|
||||||
|
d["vectors_" + name] = val
|
||||||
|
|
||||||
|
if "eval_function" in kwargs:
|
||||||
|
d["user_eval_function"] = kwargs["eval_function"]
|
||||||
|
|
||||||
|
# see shaders/utils.h for value explanation (function_mode)
|
||||||
|
if "eval_" in kwargs:
|
||||||
|
eval_ = kwargs["eval"]
|
||||||
|
if isinstance(eval_, int):
|
||||||
|
d["eval"] = eval_
|
||||||
|
elif eval_ == "norm":
|
||||||
|
d["eval"] = 3
|
||||||
|
elif eval_ == "real":
|
||||||
|
d["eval"] = 5
|
||||||
|
elif eval_ == "imag":
|
||||||
|
d["eval"] = 6
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
bezier_trig_trafos = { } # cache trafos for different orders
|
bezier_trig_trafos = {} # cache trafos for different orders
|
||||||
|
|
||||||
def BuildRenderData(mesh, func, order=2, draw_surf=True, draw_vol=True, deformation=None, region=True):
|
# def Draw(shape, clipping=None, js_code=None, filename=""):
|
||||||
d = {}
|
# # todo: also handle occ geometry, list of shapes, etc.
|
||||||
d['ngsolve_version'] = "Netgen"
|
|
||||||
d['mesh_dim'] = 3 # mesh.dim TODO
|
|
||||||
|
|
||||||
d['order2d'] = 1
|
# scene = WebGLScene(shape, clipping=clipping, on_init=js_code)
|
||||||
d['order3d'] = 0
|
|
||||||
|
|
||||||
d['draw_vol'] = False
|
# if wg._IN_IPYTHON:
|
||||||
d['draw_surf'] = True
|
# if wg._IN_GOOGLE_COLAB:
|
||||||
d['funcdim'] = 1
|
# from IPython.display import display, HTML
|
||||||
|
# html = scene.GenerateHTML()
|
||||||
func2 = None
|
# display(HTML(html))
|
||||||
if func and func.is_complex:
|
# else:
|
||||||
d['is_complex'] = True
|
# # render scene using widgets.DOMWidget
|
||||||
func1 = func[0].real
|
# scene.Draw()
|
||||||
func2 = ngs.CoefficientFunction( (func[0].imag, 0.0) )
|
# return scene
|
||||||
elif func and func.dim>1:
|
# else:
|
||||||
func1 = func[0]
|
# if filename:
|
||||||
func2 = ngs.CoefficientFunction( tuple(func[i] if i<func.dim else 0.0 for i in range(1,3)) ) # max 3-dimensional functions
|
# scene.GenerateHTML(filename=filename)
|
||||||
d['funcdim'] = func.dim
|
# return scene
|
||||||
elif func:
|
|
||||||
func1 = func
|
|
||||||
d['funcdim'] = 1
|
|
||||||
else:
|
|
||||||
# no function at all -> we are just drawing a mesh, eval mesh element index instead
|
|
||||||
mats = mesh.GetMaterials()
|
|
||||||
bnds = mesh.GetBoundaries()
|
|
||||||
nmats = len(mesh.GetMaterials())
|
|
||||||
nbnds = len(mesh.GetBoundaries())
|
|
||||||
n = max(nmats, nbnds)
|
|
||||||
func1 = ngs.CoefficientFunction(list(range(n)))
|
|
||||||
n_regions = [0, 0, nmats, nbnds]
|
|
||||||
d['mesh_regions_2d'] = n_regions[mesh.dim]
|
|
||||||
d['mesh_regions_3d'] = nmats if mesh.dim==3 else 0
|
|
||||||
d['funcdim'] = 0
|
|
||||||
func1 = ngs.CoefficientFunction( (ngs.x, ngs.y, ngs.z, func1 ) )
|
|
||||||
func0 = ngs.CoefficientFunction( (ngs.x, ngs.y, ngs.z, 0.0 ) )
|
|
||||||
if deformation is not None:
|
|
||||||
func1 += ngs.CoefficientFunction((deformation, 0.0))
|
|
||||||
func0 += ngs.CoefficientFunction((deformation, 0.0))
|
|
||||||
|
|
||||||
d['show_wireframe'] = False
|
|
||||||
d['show_mesh'] = True
|
|
||||||
if order2d>0:
|
|
||||||
og = order2d
|
|
||||||
d['show_wireframe'] = True
|
|
||||||
d['show_mesh'] = True
|
|
||||||
timer2.Start()
|
|
||||||
|
|
||||||
timer3Bvals.Start()
|
|
||||||
|
|
||||||
# transform point-values to Bernsteinbasis
|
|
||||||
def Binomial(n,i): return math.factorial(n) / math.factorial(i) / math.factorial(n-i)
|
|
||||||
def Bernstein(x, i, n): return Binomial(n,i) * x**i*(1-x)**(n-i)
|
|
||||||
Bvals = ngs.Matrix(og+1,og+1)
|
|
||||||
for i in range(og+1):
|
|
||||||
for j in range(og+1):
|
|
||||||
Bvals[i,j] = Bernstein(i/og, j, og)
|
|
||||||
iBvals = Bvals.I
|
|
||||||
timer3Bvals.Stop()
|
|
||||||
# print (Bvals)
|
|
||||||
# print (iBvals)
|
|
||||||
|
|
||||||
|
|
||||||
Bezier_points = []
|
def _get_draw_default_args():
|
||||||
|
return dict(
|
||||||
ipts = [(i/og,0) for i in range(og+1)] + [(0, i/og) for i in range(og+1)] + [(i/og,1.0-i/og) for i in range(og+1)]
|
name="function",
|
||||||
ir_trig = ngs.IntegrationRule(ipts, [0,]*len(ipts))
|
order=2,
|
||||||
ipts = [(i/og,0) for i in range(og+1)] + [(0, i/og) for i in range(og+1)] + [(i/og,1.0) for i in range(og+1)] + [(1.0, i/og) for i in range(og+1)]
|
draw_vol=True,
|
||||||
ir_quad = ngs.IntegrationRule(ipts, [0,]*len(ipts))
|
draw_surf=True,
|
||||||
|
autoscale=True,
|
||||||
vb = [ngs.VOL, ngs.BND][mesh.dim-2]
|
deformation=False,
|
||||||
if region and region.VB() == vb:
|
interpolate_multidim=False,
|
||||||
vb = region
|
animate=False,
|
||||||
cf = func1 if draw_surf else func0
|
objects=[],
|
||||||
timer2map.Start()
|
nodal_p1=False,
|
||||||
pts = mesh.MapToAllElements({ngs.ET.TRIG: ir_trig, ngs.ET.QUAD: ir_quad}, vb)
|
settings={},
|
||||||
timer2map.Stop()
|
width=_default_width,
|
||||||
pmat = cf(pts)
|
height=_default_height,
|
||||||
|
)
|
||||||
timermult.Start()
|
|
||||||
pmat = pmat.reshape(-1, og+1, 4)
|
|
||||||
if False:
|
|
||||||
BezierPnts = np.tensordot(iBvals.NumPy(), pmat, axes=(1,1))
|
|
||||||
else:
|
|
||||||
BezierPnts = np.zeros( (og+1, pmat.shape[0], 4) )
|
|
||||||
for i in range(4):
|
|
||||||
ngsmat = ngs.Matrix(pmat[:,:,i].transpose())
|
|
||||||
BezierPnts[:,:,i] = iBvals * ngsmat
|
|
||||||
timermult.Stop()
|
|
||||||
|
|
||||||
timer2list.Start()
|
|
||||||
for i in range(og+1):
|
|
||||||
Bezier_points.append(encodeData(BezierPnts[i], dtype=np.float32))
|
|
||||||
timer2list.Stop()
|
|
||||||
|
|
||||||
d['Bezier_points'] = Bezier_points
|
|
||||||
|
|
||||||
ipts = [(i/og,0) for i in range(og+1)]
|
|
||||||
ir_seg = ngs.IntegrationRule(ipts, [0,]*len(ipts))
|
|
||||||
vb = [ngs.VOL, ngs.BND, ngs.BBND][mesh.dim-1]
|
|
||||||
if region and region.VB() == vb:
|
|
||||||
vb = region
|
|
||||||
pts = mesh.MapToAllElements(ir_seg, vb)
|
|
||||||
pmat = func0(pts)
|
|
||||||
pmat = pmat.reshape(-1, og+1, 4)
|
|
||||||
edge_data = np.tensordot(iBvals.NumPy(), pmat, axes=(1,1))
|
|
||||||
edges = []
|
|
||||||
for i in range(og+1):
|
|
||||||
edges.append(encodeData(edge_data[i], dtype=np.float32))
|
|
||||||
d['edges'] = edges
|
|
||||||
|
|
||||||
ndtrig = int((og+1)*(og+2)/2)
|
|
||||||
|
|
||||||
if og in bezier_trig_trafos.keys():
|
|
||||||
iBvals_trig = bezier_trig_trafos[og]
|
|
||||||
else:
|
|
||||||
def BernsteinTrig(x, y, i, j, n):
|
|
||||||
return math.factorial(n)/math.factorial(i)/math.factorial(j)/math.factorial(n-i-j) \
|
|
||||||
* x**i*y**j*(1-x-y)**(n-i-j)
|
|
||||||
Bvals = ngs.Matrix(ndtrig, ndtrig)
|
|
||||||
ii = 0
|
|
||||||
for ix in range(og+1):
|
|
||||||
for iy in range(og+1-ix):
|
|
||||||
jj = 0
|
|
||||||
for jx in range(og+1):
|
|
||||||
for jy in range(og+1-jx):
|
|
||||||
Bvals[ii,jj] = BernsteinTrig(ix/og, iy/og, jx, jy, og)
|
|
||||||
jj += 1
|
|
||||||
ii += 1
|
|
||||||
iBvals_trig = Bvals.I
|
|
||||||
bezier_trig_trafos[og] = iBvals_trig
|
|
||||||
|
|
||||||
|
|
||||||
# Bezier_points = [ [] for i in range(ndtrig) ]
|
def Draw(obj, *args, **kwargs):
|
||||||
Bezier_points = []
|
kwargs_with_defaults = _get_draw_default_args()
|
||||||
|
kwargs_with_defaults.update(kwargs)
|
||||||
ipts = [(i/og,j/og) for j in range(og+1) for i in range(og+1-j)]
|
|
||||||
ir_trig = ngs.IntegrationRule(ipts, [0,]*len(ipts))
|
|
||||||
ipts = ([(i/og,j/og) for j in range(og+1) for i in range(og+1-j)] +
|
|
||||||
[(1-i/og,1-j/og) for j in range(og+1) for i in range(og+1-j)])
|
|
||||||
ir_quad = ngs.IntegrationRule(ipts, [0,]*len(ipts))
|
|
||||||
|
|
||||||
vb = [ngs.VOL, ngs.BND][mesh.dim-2]
|
|
||||||
if region and region.VB() == vb:
|
|
||||||
vb = region
|
|
||||||
pts = mesh.MapToAllElements({ngs.ET.TRIG: ir_trig, ngs.ET.QUAD: ir_quad}, vb)
|
|
||||||
|
|
||||||
pmat = ngs.CoefficientFunction( func1 if draw_surf else func0 ) (pts)
|
|
||||||
|
|
||||||
|
|
||||||
funcmin = np.min(pmat[:,3])
|
|
||||||
funcmax = np.max(pmat[:,3])
|
|
||||||
pmin = np.min(pmat[:,0:3], axis=0)
|
|
||||||
pmax = np.max(pmat[:,0:3], axis=0)
|
|
||||||
|
|
||||||
mesh_center = 0.5*(pmin+pmax)
|
|
||||||
mesh_radius = np.linalg.norm(pmax-pmin)/2
|
|
||||||
|
|
||||||
pmat = pmat.reshape(-1, len(ir_trig), 4)
|
|
||||||
|
|
||||||
BezierPnts = np.tensordot(iBvals_trig.NumPy(), pmat, axes=(1,1))
|
|
||||||
|
|
||||||
for i in range(ndtrig):
|
|
||||||
Bezier_points.append(encodeData(BezierPnts[i], dtype=np.float32))
|
|
||||||
|
|
||||||
|
|
||||||
d['Bezier_trig_points'] = Bezier_points
|
|
||||||
d['mesh_center'] = list(mesh_center)
|
|
||||||
d['mesh_radius'] = mesh_radius
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if func:
|
|
||||||
d['funcmin'] = funcmin
|
|
||||||
d['funcmax'] = funcmax
|
|
||||||
return d
|
|
||||||
|
|
||||||
def Draw(shape, clipping=None, js_code=None, filename=""):
|
|
||||||
# todo: also handle occ geometry, list of shapes, etc.
|
|
||||||
|
|
||||||
scene = WebGLScene(shape, clipping=clipping, on_init=js_code)
|
|
||||||
|
|
||||||
|
scene = WebGLScene(obj, args, kwargs_with_defaults)
|
||||||
if wg._IN_IPYTHON:
|
if wg._IN_IPYTHON:
|
||||||
if wg._IN_GOOGLE_COLAB:
|
if wg._IN_GOOGLE_COLAB:
|
||||||
from IPython.display import display, HTML
|
from IPython.display import display, HTML
|
||||||
|
|
||||||
html = scene.GenerateHTML()
|
html = scene.GenerateHTML()
|
||||||
display(HTML(html))
|
display(HTML(html))
|
||||||
else:
|
else:
|
||||||
|
import webgui_jupyter_widgets as wjw
|
||||||
|
from packaging.version import parse
|
||||||
|
|
||||||
# render scene using widgets.DOMWidget
|
# render scene using widgets.DOMWidget
|
||||||
scene.Draw()
|
if parse(wjw.__version__) < parse("0.2.15"):
|
||||||
|
scene.Draw()
|
||||||
|
else:
|
||||||
|
scene.Draw(
|
||||||
|
kwargs_with_defaults["width"], kwargs_with_defaults["height"]
|
||||||
|
)
|
||||||
return scene
|
return scene
|
||||||
else:
|
else:
|
||||||
if filename:
|
if "filename" in kwargs_with_defaults:
|
||||||
scene.GenerateHTML(filename=filename)
|
scene.GenerateHTML(filename=kwargs_with_defaults["filename"])
|
||||||
return scene
|
return scene
|
||||||
|
|
||||||
|
|
||||||
|
def _DrawDocu(obj, *args, **kwargs):
|
||||||
|
kwargs_with_defaults = _get_draw_default_args()
|
||||||
|
kwargs_with_defaults.update(kwargs)
|
||||||
|
scene = WebGLScene(obj, args, kwargs)
|
||||||
|
import json
|
||||||
|
|
||||||
|
docu_path = os.environ["NETGEN_DOCUMENTATION_OUT_DIR"]
|
||||||
|
src_path = os.environ["NETGEN_DOCUMENTATION_SRC_DIR"]
|
||||||
|
cwd_path = os.path.abspath(".")
|
||||||
|
rel_path = os.path.relpath(".", src_path)
|
||||||
|
path = os.path.join(docu_path, rel_path)
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
counter_file = os.path.join(docu_path, ".counter")
|
||||||
|
if os.path.exists(counter_file):
|
||||||
|
file_counter = int(open(counter_file, "r").read()) + 1
|
||||||
|
else:
|
||||||
|
file_counter = 0
|
||||||
|
|
||||||
|
open(counter_file, "w").write(str(file_counter))
|
||||||
|
|
||||||
|
data_file = "render_data_{}.json".format(file_counter)
|
||||||
|
data_file_abs = os.path.join(path, data_file)
|
||||||
|
preview_file = "preview_{}.png".format(file_counter)
|
||||||
|
preview_file_abs = os.path.join(path, preview_file)
|
||||||
|
|
||||||
|
widget = WebGuiDocuWidget()
|
||||||
|
widget.value = {"render_data": data_file, "preview": preview_file}
|
||||||
|
scene.widget = widget
|
||||||
|
data = scene.GetData()
|
||||||
|
json.dump(data, open(data_file_abs, "w"))
|
||||||
|
scene.MakeScreenshot(preview_file_abs, 1200, 600)
|
||||||
|
scene.Redraw = lambda: None
|
||||||
|
from IPython.display import display, HTML
|
||||||
|
|
||||||
|
display(widget)
|
||||||
|
return scene
|
||||||
|
|
||||||
|
|
||||||
|
if "NETGEN_DOCUMENTATION_SRC_DIR" in os.environ:
|
||||||
|
# we are buiding the documentation, some things are handled differently:
|
||||||
|
# 1) Draw() is generating a .png (using headless chromium via selenium) and a render_data.json
|
||||||
|
# to show a preview image and load the render_data only when requested by user
|
||||||
|
# 2) return a NGSDocuWebGuiWidget instead of NGSWebGuiWidget implementing the preview/load on demand of webgui
|
||||||
|
|
||||||
|
_Draw = Draw
|
||||||
|
Draw = _DrawDocu
|
||||||
|
Loading…
Reference in New Issue
Block a user