import warnings
from typing import Optional, Union
import matplotlib.pyplot as plt
import numpy as np
import plotly.graph_objs as go
from matplotlib.colors import to_hex
from ...post import load_model_data
from ...utils import CONFIGS, get_bounds, gram_schmidt
from .plot_resp_base import (
PlotResponsePlotlyBase,
_make_lines_arrows,
_plot_bc,
_plot_mp_constraint,
)
from .plot_utils import (
PLOT_ARGS,
_get_ele_color,
_make_lines_plotly,
_plot_lines,
_plot_points,
_plot_unstru,
)
PKG_NAME = CONFIGS.get_pkg_name()
class PlotModelBase(PlotResponsePlotlyBase):
def __init__(self, model_info: dict, cells: dict):
# --------------------------------------------------------------
self._set_model_data(model_info, cells)
# -------------------------------------------------------------
self.pargs = PLOT_ARGS
self.FIGURE = go.Figure()
def _set_model_data(self, model_info: dict, cells: dict):
self.nodal_data = model_info.get("NodalData", [])
if len(self.nodal_data) > 0:
self.nodal_tags = self.nodal_data.coords["nodeTags"].values
self.points = self.nodal_data.to_numpy()
self.ndims = self.nodal_data.attrs["ndims"]
self.show_zaxis = not np.max(self.ndims) <= 2
self.bounds, self.min_bound_size, self.max_bound_size = get_bounds(
self.points
)
else:
raise ValueError("Model have no nodal data!") # noqa: TRY003
# -------------------------------------------------------------
self.ele_centers = model_info.get("eleCenters", [])
if len(self.ele_centers) > 0 and "eleTags" in self.ele_centers.coords:
self.ele_tags = self.ele_centers.coords["eleTags"]
else:
self.ele_tags = []
# ---------------------------------------------------------------
self.ele_data_types = cells
self.ele_types = list(cells.keys())
# -------------------------------------------------------------
self.fixed_node_data = model_info.get("FixedNodalData", [])
self.nodal_load_data = model_info.get("NodalLoadData", [])
self.frame_load_data = model_info.get("FrameLoadData", [])
self.mp_constraint_data = model_info.get("MPConstraintData", [])
# ------------------------------------------------------------
self.beam_data = model_info.get("BeamData", [])
# -------------------------------------------------------------
self.link_data = model_info.get("LinkData", [])
# -------------------------------------------------------------
self.shell_data = model_info.get("ShellData", [])
# -------------------------------------------------------------
self.line_data = model_info.get("AllLineElesData", [])
self.line_cells, self.line_tags = self._get_line_cells(self.line_data)
# -------------------------------------------------------------
self.unstru_data = model_info.get("UnstructuralData", [])
self.unstru_tags, self.unstru_cell_types, self.unstru_cells = (
self._get_unstru_cells(self.unstru_data)
)
def plot_model_one_color(
self,
plotter: list,
color: str,
style: str,
):
if len(self.unstru_data) > 0:
(
face_points,
face_line_points,
face_mid_points,
face_veci,
face_vecj,
face_veck,
) = self._get_plotly_unstru_data(
self.points, self.unstru_cell_types, self.unstru_cells
)
_plot_unstru(
plotter,
pos=face_points,
veci=face_veci,
vecj=face_vecj,
veck=face_veck,
style=style,
line_width=self.pargs.line_width,
color=color,
opacity=self.pargs.mesh_opacity,
show_edges=self.pargs.show_mesh_edges,
edge_color=self.pargs.mesh_edge_color,
edge_width=self.pargs.mesh_edge_width,
edge_points=face_line_points,
hoverinfo="skip",
)
if len(self.line_data) > 0:
line_points, line_mid_points = self._get_plotly_line_data(
self.points, self.line_cells
)
_plot_lines(
plotter,
pos=line_points,
color=color,
width=self.pargs.line_width,
hoverinfo="skip",
)
def plot_model(
self,
plotter: list,
style: str,
show_ele_hover: bool = True,
):
if len(self.ele_data_types) > 0:
colors = _get_ele_color(self.ele_types)
for i, name in enumerate(self.ele_types):
cell = np.array(self.ele_data_types[name][:, :-1], dtype=int)
cell_type = np.array(self.ele_data_types[name][:, -1], dtype=int)
if cell_type[0] in self.unstru_cell_types:
(
face_points,
face_line_points,
face_mid_points,
face_veci,
face_vecj,
face_veck,
) = self._get_plotly_unstru_data(self.points, cell_type, cell)
_plot_unstru(
plotter,
pos=face_points,
veci=face_veci,
vecj=face_vecj,
veck=face_veck,
style=style,
color=colors[i],
name=name,
hoverinfo="skip",
line_width=self.pargs.line_width,
opacity=self.pargs.mesh_opacity,
show_edges=self.pargs.show_mesh_edges,
edge_color=self.pargs.mesh_edge_color,
edge_width=self.pargs.mesh_edge_width,
edge_points=face_line_points,
)
for i, name in enumerate(self.ele_types):
cell = np.array(self.ele_data_types[name][:, :-1], dtype=int)
if cell[0, 0] == 2:
line_points, line_mid_points = self._get_plotly_line_data(
self.points, cell
)
_plot_lines(
plotter,
pos=line_points,
color=colors[i],
width=self.pargs.line_width,
name=name,
hoverinfo="skip",
)
# add element hover data
if show_ele_hover:
for i, name in enumerate(self.ele_types):
cell = np.array(self.ele_data_types[name][:, :-1], dtype=int)
ele_tags = self.ele_data_types[name].coords["eleTags"].values
ele_centers, labels = [], []
for etag, cell_ in zip(ele_tags, cell):
ntags = self.nodal_tags[cell_[1:]]
ele_centers.append(np.mean(self.points[cell_[1:]], axis=0))
labels.append(f"eleTag: {etag}<br>connectedNodes:<br> {ntags}")
size = 2 if self.pargs.point_size < 2 else self.pargs.point_size
_plot_points(
plotter,
pos=np.array(ele_centers),
color=colors[i],
size=size,
name=name,
symbol="diamond-open",
customdata=labels,
hovertemplate="%{customdata}",
)
node_labels = [str(i) for i in self.nodal_tags]
_plot_points(
plotter,
pos=self.points,
color=self.pargs.color_point,
size=self.pargs.point_size,
name="Nodes",
customdata=node_labels,
hovertemplate="x: %{x}<br>y: %{y}<br>z: %{z} <br>nodeTag: %{customdata}",
)
def plot_nodal_labels(
self,
plotter: list,
):
if len(self.nodal_data) > 0:
node_labels = [str(i) for i in self.nodal_tags]
x, y, z = self.points[:, 0], self.points[:, 1], self.points[:, 2]
txt_plot = go.Scatter3d(
x=x,
y=y,
z=z,
text=node_labels,
textfont={"color": "#6e750e", "size": self.pargs.font_size},
mode="text",
name="Node Tag",
)
plotter.append(txt_plot)
def plot_ele_labels(
self,
plotter: list,
):
if len(self.ele_centers) > 0:
ele_tags = self.ele_centers.coords["eleTags"].data
ele_centers = self.ele_centers.to_numpy()
ele_labels = ["E" + str(i) for i in ele_tags]
txt_plot = go.Scatter3d(
x=ele_centers[:, 0],
y=ele_centers[:, 1],
z=ele_centers[:, 2],
text=ele_labels,
textfont={"color": "#650021", "size": self.pargs.font_size},
mode="text",
name="Element Tag",
)
plotter.append(txt_plot)
def plot_bc(self, plotter: list, alpha: float = 1.0, points_new=None):
if len(self.fixed_node_data) > 0:
fixed_data = self.fixed_node_data.to_numpy()
fixed_dofs = fixed_data[:, -6:].astype(int)
fixed_coords = points_new if points_new is not None else fixed_data[:, :3]
s = (self.max_bound_size + self.min_bound_size) / 100 * alpha
bc_plot = _plot_bc(
plotter,
fixed_dofs,
fixed_coords,
s,
show_zaxis=self.show_zaxis,
color=self.pargs.color_bc,
)
return bc_plot
else:
return None
def plot_link(self, plotter: list):
if len(self.link_data) == 0:
return None
cells = self.link_data[:, :3]
points_zero = []
points_nonzero = []
cells_nonzero = []
for cell in cells:
idx1, idx2 = cell[1:]
idx1, idx2 = int(idx1), int(idx2)
coord1, coord2 = self.points[idx1], self.points[idx2]
length = np.sqrt(np.sum((coord2 - coord1) ** 2))
if np.abs(length) < 1e-8:
points_zero.append(coord1)
else:
xaxis = np.array(coord2 - coord1)
global_z = [0.0, 0.0, 1.0]
cos_angle = xaxis.dot(global_z) / (
np.linalg.norm(xaxis) * np.linalg.norm(global_z)
)
if np.abs(1 - cos_angle**2) < 1e-10:
yaxis = np.cross([-1.0, 0.0, 0.0], xaxis)
else:
yaxis = np.cross(global_z, xaxis)
xaxis = xaxis / np.linalg.norm(xaxis)
yaxis = yaxis / np.linalg.norm(yaxis)
idx = len(points_nonzero)
for i in range(5):
cells_nonzero.extend([2, idx + i, idx + i + 1])
points_nonzero.extend(
[
coord1 + 0.25 * length * xaxis,
coord1 + 0.25 * length * xaxis + 0.25 * length * yaxis,
coord1 + 0.5 * length * xaxis - 0.25 * length * yaxis,
coord1 + 0.5 * length * xaxis + 0.25 * length * yaxis,
coord1 + 0.75 * length * xaxis - 0.25 * length * yaxis,
coord1 + 0.75 * length * xaxis,
]
)
# plot
if len(points_zero) > 0:
points_zero = np.array(points_zero)
plotter.append(
go.Scatter3d(
x=points_zero[:, 0],
y=points_zero[:, 1],
z=points_zero[:, 2],
marker={
"size": self.pargs.point_size * 2,
"color": self.pargs.color_link,
},
mode="markers",
hoverinfo="skip",
)
)
if len(points_nonzero) > 0:
cells_nonzero = np.reshape(cells_nonzero, (-1, 3))
points_nonzero = np.array(points_nonzero)
line_points, _ = _make_lines_plotly(points_nonzero, cells_nonzero)
x, y, z = line_points[:, 0], line_points[:, 1], line_points[:, 2]
plotter.append(
go.Scatter3d(
x=x,
y=y,
z=z,
line={
"color": self.pargs.color_link,
"width": self.pargs.line_width / 2,
},
mode="lines",
connectgaps=False,
hoverinfo="skip",
)
)
@staticmethod
def _plot_local_axis(plotter, xaxis, yaxis, zaxis, midpoints, lengths):
if len(midpoints) > 0:
arrow_heights = [0.4 * ll for ll in lengths]
arrow_widths = [0.8 * h for h in arrow_heights]
plotter.extend(
_make_lines_arrows(
midpoints,
lengths,
xaxis,
yaxis,
zaxis,
"#cf6275",
"Local Axis",
["x"] * len(midpoints),
1.0,
arrow_heights,
arrow_widths,
)
)
plotter.extend(
_make_lines_arrows(
midpoints,
lengths,
yaxis,
xaxis,
zaxis,
"#04d8b2",
"Local Axis",
["y"] * len(midpoints),
1.0,
arrow_heights,
arrow_widths,
)
)
plotter.extend(
_make_lines_arrows(
midpoints,
lengths,
zaxis,
xaxis,
yaxis,
"#9aae07",
"Local Axis",
["z"] * len(midpoints),
1.0,
arrow_heights,
arrow_widths,
)
)
else:
warnings.warn(
"Model has no frame elements when show_local_crd=True!", stacklevel=2
)
def plot_link_local_axes(self, plotter: list, alpha: float = 1.0):
if len(self.link_data) == 0:
return None
lengths = self.link_data.loc[:, "length"].to_numpy()
lengths = [np.mean(lengths) / 5 * alpha] * len(lengths)
self._plot_local_axis(
plotter,
self.link_data.loc[:, ["xaxis-x", "xaxis-y", "xaxis-z"]].to_numpy(),
self.link_data.loc[:, ["yaxis-x", "yaxis-y", "yaxis-z"]].to_numpy(),
self.link_data.loc[:, ["zaxis-x", "zaxis-y", "zaxis-z"]].to_numpy(),
self.link_data.loc[:, ["xo", "yo", "zo"]].to_numpy(),
lengths,
)
def plot_beam_local_axes(self, plotter: list, alpha: float = 1.0):
if len(self.beam_data) == 0:
return None
lengths = self.beam_data.loc[:, "length"].to_numpy()
lengths = [np.mean(lengths) / 5 * alpha] * len(lengths)
self._plot_local_axis(
plotter,
self.beam_data.loc[:, ["xaxis-x", "xaxis-y", "xaxis-z"]].to_numpy(),
self.beam_data.loc[:, ["yaxis-x", "yaxis-y", "yaxis-z"]].to_numpy(),
self.beam_data.loc[:, ["zaxis-x", "zaxis-y", "zaxis-z"]].to_numpy(),
self.beam_data.loc[:, ["xo", "yo", "zo"]].to_numpy(),
lengths,
)
def plot_shell_local_axes(self, plotter: list, alpha: float = 1.0):
if len(self.shell_data) == 0:
return None
node_coords = self.points
cells = self.shell_data.to_numpy()[:, :-1]
xlocal, ylocal, zlocal, midpoints, lengths = [], [], [], [], []
for cell in cells:
num = len(cell) - 1
coord = node_coords[cell[1:]]
if num == 3:
coord_ = coord
v1, v2 = coord_[1] - coord_[0], coord_[2] - coord_[0]
elif num == 6:
coord_ = coord[[0, 2, 4]]
v1, v2 = coord_[1] - coord_[0], coord_[2] - coord_[0]
elif num == 4:
coord_ = coord
v1 = 0.5 * ((coord_[1] + coord_[2]) - (coord_[0] + coord_[3]))
v2 = 0.5 * ((coord_[2] + coord_[3]) - (coord_[0] + coord_[1]))
else:
coord_ = coord[[0, 2, 4, 6]]
v1 = 0.5 * ((coord_[1] + coord_[2]) - (coord_[0] + coord_[3]))
v2 = 0.5 * ((coord_[2] + coord_[3]) - (coord_[0] + coord_[1]))
x, y, z = gram_schmidt(v1, v2)
xyzo = np.mean(coord, axis=0)
xlocal.append(x)
ylocal.append(y)
zlocal.append(z)
midpoints.append(xyzo)
lengths.append((np.linalg.norm(v1) + np.linalg.norm(v2)) / 2)
xaxis, yaxis, zaxis = np.array(xlocal), np.array(ylocal), np.array(zlocal)
midpoints, lengths = np.array(midpoints), np.array(lengths)
lengths = [np.mean(lengths) / 5 * alpha] * len(lengths)
self._plot_local_axis(
plotter,
xaxis,
yaxis,
zaxis,
midpoints,
lengths,
)
def plot_node_load(self, plotter: list, alpha: float = 1.0):
if len(self.nodal_load_data) == 0:
return None
pntags = self.nodal_load_data.coords["PatternNodeTags"].values
patterntags, nodetags = [], []
for item in pntags:
num1, num2 = item.split("-")
patterntags.append(int(num1))
nodetags.append(int(num2))
patterntags, nodetags = np.array(patterntags), np.array(nodetags)
load_data = self.nodal_load_data.to_numpy()
maxdata = np.max(np.abs(load_data))
alpha_ = (self.max_bound_size + self.min_bound_size) / 20 / maxdata
alpha_ *= alpha
patterntags2 = np.unique(patterntags)
cmap = plt.get_cmap("winter")
colors = cmap(np.linspace(0, 1, len(patterntags2)))
xyzlocals = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
idxs = [[0, 1, 2], [1, 0, 2], [2, 0, 1]]
for p, ptag in enumerate(patterntags2):
idx = np.abs(patterntags - ptag) < 1e-4
ntags = nodetags[idx]
coords = self.nodal_data.loc[ntags, :].to_numpy()
for i in range(3):
data = np.ravel(load_data[idx, i])
lengths = np.abs(data) * alpha_
arrow_heights = [0.5 * ll for ll in lengths]
arrow_widths = [0.8 * h for h in arrow_heights]
xaxis = np.reshape(xyzlocals[idxs[i][0]] * len(coords), (-1, 3))
yaxis = np.reshape(xyzlocals[idxs[i][1]] * len(coords), (-1, 3))
zaxis = np.reshape(xyzlocals[idxs[i][2]] * len(coords), (-1, 3))
new_coords = np.zeros_like(coords)
for j in range(len(xaxis)):
xaxis[j] *= np.sign(data[j])
new_coords[j] = coords[j] - 1.4 * lengths[j] * xaxis[j]
labels = [f"{d:.2e}" for d in data]
color = colors[p]
plotter.extend(
_make_lines_arrows(
new_coords,
lengths,
xaxis,
yaxis,
zaxis,
to_hex(color),
f"<b>Pattern {ptag}</b><br>Nodal Load",
labels,
self.pargs.line_width,
arrow_heights,
arrow_widths,
)
)
def plot_ele_load(self, plotter: list, alpha: float = 1.0):
if len(self.frame_load_data) == 0:
return None
petags = self.frame_load_data.coords["PatternEleTags"].values
patterntags, eletags = [], []
for item in petags:
num1, num2 = item.split("-")
patterntags.append(int(num1))
eletags.append(int(num2))
patterntags, eletags = np.array(patterntags), np.array(eletags)
patterntags2 = np.unique(patterntags)
load_info = self.frame_load_data.to_numpy()
new_points = []
new_locals = []
new_ptags = []
load_data = []
for i, ptag in enumerate(patterntags):
node1, node2 = load_info[i, :2]
coord1, coord2 = (
self.nodal_data.loc[node1, :],
self.nodal_data.loc[node2, :],
)
wya, wyb, wza, wzb, wxa, wxb, xa, xb = load_info[i, 2:]
etag = eletags[i]
local_axis = self.beam_data.loc[
etag,
[
"xaxis-x",
"xaxis-y",
"xaxis-z",
"yaxis-x",
"yaxis-y",
"yaxis-z",
"zaxis-x",
"zaxis-y",
"zaxis-z",
],
].to_numpy()
if xb > xa: # distributed load
n = np.max([int((xb - xa) / 0.1) + 1, 6])
xl = np.linspace(xa, xb, n)
wz = np.interp(xl, [xa, xb], [wza, wzb])
wy = np.interp(xl, [xa, xb], [wya, wyb])
wx = np.interp(xl, [xa, xb], [wxa, wxb])
localaxis = [local_axis] * n
new_ptags.extend([ptag] * n)
else:
xl = [xa]
wx, wy, wz = wxa, wya, wza
localaxis = [local_axis]
new_ptags.append(ptag)
xs = np.interp(xl, [0, 1], [coord1[0], coord2[0]])
ys = np.interp(xl, [0, 1], [coord1[1], coord2[1]])
zs = np.interp(xl, [0, 1], [coord1[2], coord2[2]])
new_points.append(np.column_stack([xs, ys, zs]))
new_locals.append(localaxis)
load_data.append(np.column_stack([wx, wy, wz]))
new_points = np.vstack(new_points)
new_locals = np.vstack(new_locals)
load_data = np.vstack(load_data)
new_ptags = np.array(new_ptags)
maxdata = np.max(np.abs(load_data))
alpha_ = (self.max_bound_size + self.min_bound_size) / 20 / maxdata
alpha_ *= alpha
cmap = plt.get_cmap("turbo_r")
colors = cmap(np.linspace(0, 1, len(patterntags2)))
idxs = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
idxsidx = [[0, 1, 2], [1, 0, 2], [2, 0, 1]]
for p, ptag in enumerate(patterntags2):
idx = np.abs(new_ptags - ptag) < 1e-3
coords = new_points[idx]
for i in range(3):
data = np.ravel(load_data[idx, i])
lengths = np.abs(data) * alpha_
arrow_heights = [0.5 * ll for ll in lengths]
arrow_widths = [0.8 * h for h in arrow_heights]
xaxis = new_locals[np.ix_(idx, idxs[idxsidx[i][0]])]
yaxis = new_locals[np.ix_(idx, idxs[idxsidx[i][1]])]
zaxis = new_locals[np.ix_(idx, idxs[idxsidx[i][2]])]
new_coords = np.zeros_like(coords)
for j in range(len(xaxis)):
xaxis[j] *= np.sign(data[j])
new_coords[j] = coords[j] - 1.4 * lengths[j] * xaxis[j]
labels = [f"{d:.2e}" for d in data]
plotter.extend(
_make_lines_arrows(
new_coords,
lengths,
xaxis,
yaxis,
zaxis,
to_hex(colors[p]),
f"<b>Pattern {ptag}</b><br>Element Load",
labels,
self.pargs.line_width,
arrow_heights,
arrow_widths,
)
)
def plot_mp_constraint(self, plotter: list, show_dofs=False, points_new=None):
if len(self.mp_constraint_data) == 0:
return None
points = self.points if points_new is None else points_new
cells = self.mp_constraint_data.to_numpy()[:, :3].astype(int)
dofs = self.mp_constraint_data.to_numpy()[:, -6:].astype(int)
# midcoords = self.mp_constraint_data.to_numpy()[:, 3:6]
_plot_mp_constraint(
plotter,
points,
cells,
dofs,
self.pargs.line_width / 2,
self.pargs.color_constraint,
show_dofs=show_dofs,
)
def update_fig(self, plotter: list, show_outline: bool = False):
self.FIGURE.add_traces(plotter)
if not self.show_zaxis:
scene = self._get_plotly_dim_scene(
mode="2d", show_outline=show_outline, pad_ratio=0.15
)
else:
scene = self._get_plotly_dim_scene(
mode="3d", show_outline=show_outline, pad_ratio=0.15
)
txt = f"<b>{PKG_NAME}</b>:: Num. Node: <b>{len(self.nodal_tags)}</b> Num. Ele: <b>{len(self.ele_tags)}</b>"
self.FIGURE.update_layout(
template=self.pargs.theme,
autosize=True,
showlegend=False,
scene=scene,
title={
"font": {"family": "courier", "size": self.pargs.title_font_size},
"text": txt,
},
width=self.pargs.window_size[0],
height=self.pargs.window_size[1],
font={"family": self.pargs.font_family},
)
return self.FIGURE
[docs]
def plot_model(
odb_tag: Optional[Union[int, str]] = None,
show_node_numbering: bool = False,
show_ele_numbering: bool = False,
show_ele_hover: bool = True,
style: str = "surface",
color: Optional[str] = None,
show_bc: bool = True,
bc_scale: float = 1.0,
show_link: bool = True,
show_mp_constraint: bool = True,
show_constraint_dofs: bool = False,
show_nodal_loads: bool = False,
show_ele_loads: bool = False,
load_scale: float = 1.0,
show_local_axes: bool = False,
local_axes_scale: float = 1.0,
show_outline: bool = True,
) -> go.Figure:
"""
Geometric model visualization based on ``plotly``.
Parameters
----------
odb_tag: Union[int, str], default: None
Tag of output databases (ODB) to be visualized.
If None, data will be extracted from the current running memory..
show_node_numbering: bool, default: False
Whether to display node tag labels.
show_ele_numbering: bool, default: False
Whether to display element tag labels.
show_ele_hover: bool, default: True
Whether to display element tag labels when hovering over the element.
style: str, default: surface
Visualization mesh style of surfaces and solids.
One of the following: style='surface' or style='wireframe'
Defaults to 'surface'. Note that 'wireframe' only shows a wireframe of the outer geometry.
color: str, default: black
Model display color.
show_bc: bool, default: True
Whether to display boundary supports.
bc_scale: float, default: 1.0
Scale the size of boundary support display.
show_link: bool, default: True
Whether to show link elements.
show_mp_constraint: bool, default: True
Whether to show multipoint (MP) constraint.
show_constraint_dofs: bool, default: False
Whether to show dofs of mp-constraints.
show_nodal_loads: bool, default: False
Whether to show nodal loads.
show_ele_loads: bool, default: False
Whether to show element loads.
load_scale: float, default: 1.0
Scale the size of load arrow presentation.
show_local_axes: bool, default: False
Whether to display element local axes, including ``beam-column``, ``link``, and ``shell`` elements.
local_axes_scale: float, default: 1.0
Scales the presentation size of the local axes.
show_outline: bool, default: False
Whether to display the outline of the model.
Returns
-------
fig: `plotly.graph_objects.Figure <https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html>`_
You can use `fig.show()` to display,
You can also use `fig.write_html("path/to/file.html")` to save as an HTML file, see
`Interactive HTML Export in Python <https://plotly.com/python/interactive-html-export/>`_
"""
resave = odb_tag is None
model_info, cells = load_model_data(odb_tag, resave=resave)
plotbase = PlotModelBase(model_info, cells)
plotter = []
if color: # single color
plotbase.plot_model_one_color(
plotter,
color,
style,
)
else:
plotbase.plot_model(plotter, style, show_ele_hover=show_ele_hover)
if show_node_numbering:
plotbase.plot_nodal_labels(plotter)
if show_ele_numbering:
plotbase.plot_ele_labels(plotter)
if show_bc:
plotbase.plot_bc(plotter, bc_scale)
if show_mp_constraint:
plotbase.plot_mp_constraint(plotter, show_constraint_dofs)
if show_link:
plotbase.plot_link(plotter)
if show_local_axes:
plotbase.plot_beam_local_axes(plotter, local_axes_scale)
plotbase.plot_link_local_axes(plotter, local_axes_scale)
plotbase.plot_shell_local_axes(plotter, local_axes_scale)
if show_nodal_loads:
plotbase.plot_node_load(plotter, load_scale)
if show_ele_loads:
plotbase.plot_ele_load(plotter, load_scale)
return plotbase.update_fig(plotter, show_outline)