import matplotlib.pyplot as plt

import bluemira.geometry.tools
from bluemira import display
from bluemira.base.components import Component, PhysicalComponent
from bluemira.display.plotter import FacePlotter, WirePlotter
from bluemira.geometry.face import BluemiraFace
from bluemira.geometry.parameterisations import PrincetonD

Plotting examples

Setup

Creation of a closed wire and respective face and discretisation points.

PrincetonD parameterisation is used as example.

Note: the curve is generated on the x-z plane

p = PrincetonD()
p.adjust_variable("x1", 4, lower_bound=3, upper_bound=5)
p.adjust_variable("x2", 16, lower_bound=10, upper_bound=20)
p.adjust_variable("dz", 0, lower_bound=0, upper_bound=0)
wire = p.create_shape()
face = BluemiraFace(wire)

Default plotting

We can plot the list of points, as well as the BluemiraWire and BluemiraFace in the following way, using the display built-in function with default settings

display.plot_2d(wire)
display.plot_3d(wire)
display.plot_2d(face)
display.plot_3d(face)

In a similar way, it is possible to use specific Plotters for each entity, i.e. PointsPlotter, WirePlotter, and FacePlotter. For example:

plotter_2d = WirePlotter()
plotter_2d.plot_2d(wire)

Modifying defaults

Default plot options can be obtained in form of a dictionary instancing one of the plotters, e.g.:

my_options = display.plotter.get_default_options()
print(my_options)

Modifying the dictionary and passing it to a plot function will display the plot with the new options

my_options.wire_options = {"color": "red", "linewidth": "1.5"}
display.plot_2d(wire, my_options)

Alternatively, plot options can be modified directly inside a Plotter, e.g.:

plotter_2d.options.show_points = True
plotter_2d.options.number_points = True
plotter_2d.options.ndiscr = 15
plotter_2d.plot_2d(wire)

Once you get familiar with the options, you can also make your own dictionaries, and pass them to the plotting functions

my_options = {
    "show_points": False,
    "wire_options": {"color": "red", "linewidth": 3, "linestyle": "dashed"},
}
display.plot_2d(wire, **my_options)

Being matplotlib the default plot library, points_options, wire_options, and face_options are equivalent to the **kwargs passed to the functions scatter, plot, and fill, respectively.

Discretise the wire to an array of points.

points = wire.discretise(ndiscr=10, byedges=True)

Points Plot

Simple plot of the obtained points.

A 2D plot with the built-in display functions

display.plot_2d(
    points, point_options={"s": 30, "facecolors": "red", "edgecolors": "black"}
)
# or with a Plotter
# pplotter = PointsPlotter(
#     point_options={"s": 30, "facecolors": "red", "edgecolors": "black"}
# )
# pplotter.plot_2d(points)

3D Scatter Plot

A plot of the same points, but in 3D this time.

display.plot_3d(
    points, point_options={"s": 30, "facecolors": "red", "edgecolors": "black"}
)

# pplotter = Plotter3D(
#      point_options={"s": 30, "facecolors": "red", "edgecolors": "black"}
# )
# pplotter.plot_3d(points)

Wire Plot

A WirePlotter is used with the default setup with:

  • view = xz (this is the projection plane, not a section plane)

  • point size = 20

  • ndiscr = 15

wplotter = WirePlotter(view="xz")
wplotter.options.point_options["s"] = 20
wplotter.options.ndiscr = 15
wplotter.plot_2d(wire)

3D Curve Plot

A plot of the same wire, but in 3D this time.

display.plot_3d(wire, **wplotter.options.as_dict())

Wire Plot with Matplotlib Default Options

In this example point_options is set to an empty dict. The default matplotlib are used.

display.plot_2d(wire, point_options={})
# The plot is immediately shown by default, so it is not possible to act on the plot

Wire plot with some modifications

In this example, we choose our own matplotlib Axes onto which to plot, disable the automatic display of the plot (show=False), and apply a title to the plot

f, ax = plt.subplots()
wplotter.options.point_options = {}
wplotter.plot_2d(wire, ax=ax, show=False)
ax.set_title("Wire plot")
plt.show()

Face Plot

A FacePlotter is used with the default setup with:

  • view = xz (this is the projection plane, not a section plane)

  • ndiscr = 30

  • plot title

f, ax = plt.subplots()
fplotter = FacePlotter(view="xz")
fplotter.options.ndiscr = 30
fplotter.plot_2d(face, ax=ax, show=False)
ax.set_title("Face plot without points (default)")
plt.show()

Face Plot with Points

We’ve set the points to be deactivate by default, but we can enable them again for individual plotters.

f, ax = plt.subplots()
fplotter2 = FacePlotter(view="xz")
fplotter2.options.ndiscr = 30
fplotter2.options.show_points = True
fplotter2.plot_2d(face, ax=ax, show=False)
ax.set_title("Face plot with points")
plt.show()

Face Plot with only Points

Only show the wire of a face

f, ax = plt.subplots()
fplotter3 = FacePlotter(view="xz")
fplotter3.options.show_wires = True
fplotter3.options.show_faces = False
fplotter3.plot_2d(face, ax=ax, show=False)
ax.set_title("Face plot its wire")
plt.show()

Make a Second Face

A second geometry is created, surrounding our original face.

p2 = PrincetonD()
p2.adjust_variable("x1", 3.5, lower_bound=3, upper_bound=5)
p2.adjust_variable("x2", 17, lower_bound=10, upper_bound=20)
p2.adjust_variable("dz", 0, lower_bound=0, upper_bound=0)
wire2 = p2.create_shape()
face2 = BluemiraFace(wire2)

Combined Face Plot

Face and face2 are plotted using the same FacePlotter. Since no plot options have been changed, the two faces will be plotted in the same way (e.g. same color).

fplotter4 = FacePlotter(view="xz")
fplotter4.options.show_points = True
fplotter4.options.face_options = {"color": "blue"}

f, ax = plt.subplots()
fplotter4.plot_2d(face, ax=ax, show=False)
fplotter4.plot_2d(face2, ax=ax, show=False)
ax.set_title("Both faces in blue")
plt.show()
print(f"fplotter2.options: {fplotter2.options}")

Combined Face Plot with Different Colours

Plot both face with different colour.

Note: if face is plotted before face2, face2 will be “covered” by face.

f, ax = plt.subplots()
fplotter4.options.face_options = {"color": "blue"}
fplotter4.plot_2d(face2, ax=ax, show=False)
fplotter4.options.face_options = {"color": "green"}
fplotter4.plot_2d(face, ax=ax, show=False)
ax.set_title("Both faces with different colors")
plt.show()

Face with Hole

A third face is create as difference between face and face2 (a BluemiraFace object has been created using wire2 as outer boundary and wire as inner boundary).

Note:

  • when plotting points, it can happen that markers are not centred properly as described in https://github.com/matplotlib/matplotlib/issues/11836

  • face3 is created with a wire deepcopy in order to be able to modify face and face2 (and thus wire and wire2) without modifying face3

f, ax = plt.subplots()
face3 = BluemiraFace([wire2.deepcopy(), wire.deepcopy()])
fplotter5 = FacePlotter(view="xz")
fplotter5.options.show_points = True
ax = fplotter5.plot_2d(face3, ax=ax, show=False)
ax.set_title("Face with hole - points enabled")
plt.show()

f, ax = plt.subplots()
fplotter5.options.face_options["color"] = "blue"
fplotter5.options.show_points = False
fplotter5.plot_2d(face3, ax=ax, show=False)
ax.set_title("Face with hole - points disabled - blue")
plt.show()

Perform Some Face Operations

Scale and move our face, if you run the above face plots again you can see they will change.

bari = face.center_of_mass
face.scale(0.5)
new_bari = face.center_of_mass
diff = bari - new_bari
v = (diff[0], diff[1], diff[2])
face.translate(v)

Wires and Faces

Create and plot a couple of Wires and then create and plot the corresponding Faces.

points = [[0, 0, 0], [1, 0, 0], [1, 0, 3], [0, 0, 3]]
wire = bluemira.geometry.tools.make_polygon(points, closed=True)
wire1 = wire.deepcopy()
wire1.translate((3, 0, 5))
wplotter2 = WirePlotter(view="xz")
ax = wplotter2.plot_2d(wire, show=False)
ax = wplotter2.plot_2d(wire1, ax=ax, show=False)
ax.set_title("Two wires")
plt.show()

Plots with Matplotlib Default Point Options

Plot the points on a boundary of a face with matplotlib defaults.

Note that, since point_options = {}, points color is automatically changed by matplotlib.

wface = BluemiraFace(wire)
w1face = BluemiraFace(wire1)

PhysicalComponent Plot

Creates a PhysicalComponent and plots it in the xz plane

Note that if no face colour is set, a colour from the default palette will be chosen by default. This will not be the same every time.

pd_phycomp = PhysicalComponent("Comp", face)
pd_phycomp.plot_options.view = "xz"
pd_phycomp.plot_options.ndiscr = 30
ax = pd_phycomp.plot_2d(show=False)
ax.set_title("test component plot")
plt.show(block=True)

this time plots only the wire and not the face.

Note that unlike the FacePlotter when show_faces = False the wire is shown by default.

pd_phycomp = PhysicalComponent("Comp", face)
pd_phycomp.plot_options.view = "xz"
pd_phycomp.plot_options.show_faces = False
ax = pd_phycomp.plot_2d(show=False)
ax.set_title("test component plot wire of face")
plt.show(block=True)

Component Plot

Creates a Component and plots it in the xz plane using matplotlib defaults. Here we override some defaults and make our custom set of plot options. The custom setting of red loses out in priority to c3 comp being set specifically to green. In latter 2 plots shows that returns to component default settings when custom options are default or removed.

group = Component("Components")
my_group_options = group.plot_options.as_dict()
my_group_options["wire_options"] = {}
my_group_options["face_options"] = {"color": "red"}
c1 = PhysicalComponent("Comp1", face, parent=group)
c2 = PhysicalComponent("Comp2", wface, parent=group)
c3 = PhysicalComponent("Comp3", w1face, parent=group)
c3.plot_options.face_options["color"] = "green"
display.plot_2d(group, **my_group_options)
my_group_options["face_options"] = {"color": "blue"}
display.plot_2d(group, **my_group_options)
my_group_options["face_options"] = {}
display.plot_2d(group, **my_group_options)

Note that, since wire_options = {}, wire color is automatically changed by matplotlib

Component and BluemiraGeo Combined Plot

Plots a component on the same axes as a BluemiraFace.

wplotter3 = WirePlotter(view="xz")
wplotter3.options.point_options["s"] = 20
wplotter3.options.ndiscr = 15
wplotter3.options.wire_options["color"] = "red"
ax = wplotter3.plot_2d(wface.boundary[0], show=False)

fplotter6 = FacePlotter(view="xz")
fplotter6.options.show_wires = True
fplotter6.options.face_options["color"] = "green"
fplotter6.options.wire_options["color"] = "black"
ax = fplotter6.plot_2d(w1face, ax=ax, show=False)

ax = pd_phycomp.plot_2d(ax=ax, show=False)
ax.set_title("test component + BluemiraGeo plot")
plt.show(block=True)

Show the options from our combined plot

print(f"wire plotter options: {wplotter3.options}")
print(f"face plotter options: {fplotter6.options}")
print(f"component plotter options: {pd_phycomp.plot_options}")

CAD Display

BluemiraWire and BluemiraFace can be displayed as CAD using the built-in display function:

display.show_cad(face)

For what concern Components, the component function show_cad is used.

Note that if no colour is set, a colour from the default palette will be chosen by default. This will not be the same every time.

group.show_cad()

We can also change the appearance of individual components inside the group. Colours can be specified as an R-G-B tuple, string, or hex-string.

c1.display_cad_options.modify(color=(0.1, 0.2, 0.4))
c2.display_cad_options.modify(color="g")
c3.display_cad_options.modify(color="#FF3450", transparency=0.5)

group.show_cad()