#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Geometry plotter using pylab (matplotlib).
:Date: 2019-12-23
.. module:: geometry_plot_pylab
:platform: *nix, Windows
:synopsis: Geometry plotting (pylab).
.. moduleauthor:: Daniel Weschke <daniel.weschke@directbox.de>
"""
import math
# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D
assert Axes3D # silence pyflakes
import pylab
pylab.style.use('dark_background')
pylab.rcParams['text.color'] = 'grey' # lightgrey
pylab.rcParams['axes.edgecolor'] = 'grey' # lightgrey
pylab.rcParams['grid.color'] = 'grey' # lightgrey
pylab.rcParams['xtick.color'] = 'grey' # lightgrey
pylab.rcParams['ytick.color'] = 'grey' # lightgrey
pylab.rcParams['axes3d.grid'] = False
# default for dark_background cycler(
# 'color', ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
# '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'])
pylab.rcParams['axes.prop_cycle'] = pylab.cycler(
"color", ['#3498DB', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
'#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'])
# remove f for fullscreen toggle, default ['f', 'ctrl+f']
pylab.rcParams['keymap.fullscreen'] = ['ctrl+f']
# remove L for log/lin toggle, default ['k', 'L']
pylab.rcParams['keymap.xscale'] = ['k']
# remove l for log/lin toggle, default ['l']
pylab.rcParams['keymap.yscale'] = []
from pylab import mean
from .geometry import CS
# type of ax
# - matplotlib.axes._subplots.Axes3DSubplot)
# - matplotlib.axes.Axes
# - pylab.Axes
[docs]def set_aspect_equal(ax):
'''Make axes of 3D plot have equal scale so that spheres appear as
spheres, cubes as cubes, etc.. This is one possible solution to
Matplotlib's ax.set_aspect('equal') and ax.axis('equal') not
working for 3D.
:param ax: a pylab axis
:type ax: pylab.Axes
Source: https://stackoverflow.com/a/35126679
'''
if ax.name == '3d':
xlim = ax.get_xlim3d()
ylim = ax.get_ylim3d()
zlim = ax.get_zlim3d()
xmean = mean(xlim)
ymean = mean(ylim)
zmean = mean(zlim)
plot_radius = max([abs(lim - mean_)
for lims, mean_ in ((xlim, xmean),
(ylim, ymean),
(zlim, zmean))
for lim in lims])
ax.set_xlim3d([xmean - plot_radius, xmean + plot_radius])
ax.set_ylim3d([ymean - plot_radius, ymean + plot_radius])
ax.set_zlim3d([zmean - plot_radius, zmean + plot_radius])
else:
ax.axis('equal')
[docs]def plot_post(ax):
if isinstance(ax, pylab.Axes):
ax = [ax]
for axi in ax:
if axi.name == '3d':
#axi.w_xaxis.set_pane_color((0, 0, 0, 0))
#axi.w_yaxis.set_pane_color((0, 0, 0, 0))
#axi.w_zaxis.set_pane_color((0, 0, 0, 0))
axi.w_xaxis.pane.fill = False
axi.w_yaxis.pane.fill = False
axi.w_zaxis.pane.fill = False
set_aspect_equal(axi)
[docs]def wireframe3d(world):
fig = pylab.figure()
ax = fig.add_subplot(111, projection='3d')
[ax.plot(*i, 'C0') for i in world.wireframes_xyz()]
plot_post(ax)
[docs]def cad_wireframe(world, centering=True):
"""
Graphical projections
* Parallel projections
* Orthographic
* Multiview
* \* First-angle
* Third-angle
* Plan Elevation
* Axonometric
* \* Isometric
* Dimetric
* Trimetri
* Oblique
* Carbinet
* Cavalier
* Military
* Top-down
* Perspective projections
* 1-point
* 2-point
* 3-point
* Curvilinear
"""
if centering:
world.center()
fig = pylab.figure('geometry-cad')
fig.clf()
ax = fig.add_subplot(111)
pylab.axis('off')
pylab.subplots_adjust(left=0, right=1, top=1, bottom=0)
lps = [ax.plot(*i, 'C0')[0] for i in world.wireframes_xy()]
plot_post(ax)
# 'rotate: ←left, right, up, down, ctrl+left, ctrl+right\n' +
# 'pan: shift+left, shift+right, shift+up, shift+down\n' +
# 'zoom: ctrl+up, ctrl+down\n' +
# 'view: f (front), l (left), r (right), t (top), b (bottom)\n' +
# ' i (isometric), d (dimetric)',
h_open = pylab.text(
0+.01, 1-.015,
'rotate: [←], [→], [↑], [↓], [Ctrl][←], [Ctrl][→]\n' +
'pan: [Shift][←], [Shift][→], [Shift][↑], [Shift][↓]\n' +
'zoom: [Ctrl][↑], [Ctrl][↓]\n' +
'view: [f]ront, [l]eft, [r]ight, [t]op, [b]ottom\n' +
' [i]sometric, [d]imetric',
color='#2280c0',
horizontalalignment='left',
verticalalignment='top',
transform=fig.transFigure,
bbox=dict(facecolor='black', edgecolor='#196090', alpha=0.5),
family='monospace'
)
h_close = pylab.text(
0+.01, 1-.015, '[h]elp',
color='#2280c0',
horizontalalignment='left',
verticalalignment='top',
transform=fig.transFigure,
bbox=dict(facecolor='black', edgecolor='#196090', alpha=0.5),
family='monospace',
visible=False
)
def press(event, world, lps, h_open, h_close):
#print('key pressed:', event.key)
#sys.stdout.flush()
if event.key in [
'left', 'right', 'up', 'down',
'f', 't', 'b', 'l', 'r', 'i', 'd',
'shift+left', 'shift+right', 'shift+up', 'shift+down',
'ctrl+up', 'ctrl+down', 'ctrl+left', 'ctrl+right']:
d = world.space_diagonal()
if event.key == 'left':
world.rotate_y(-math.pi/180*10)
elif event.key == 'right':
world.rotate_y(+math.pi/180*10)
elif event.key == 'up':
world.rotate_x(-math.pi/180*10)
elif event.key == 'down':
world.rotate_x(+math.pi/180*10)
elif event.key == 'f':
world.cs(CS())
if centering:
world.center()
elif event.key == 't':
world.cs(CS.x90())
if centering:
world.center()
elif event.key == 'b':
world.cs(CS.xm90())
if centering:
world.center()
elif event.key == 'l':
world.cs(CS.y90())
if centering:
world.center()
elif event.key == 'r':
world.cs(CS.ym90())
if centering:
world.center()
elif event.key == 'i':
#theta_y = -math.pi/2/3
#theta_x = math.pi/2/3
theta_y = -math.pi/2/2
theta_x = math.asin(math.tan(math.pi/2/3))
world.cs(CS().rotate_y(theta_y).rotate_x(theta_x))
if centering:
world.center()
elif event.key == 'd':
#theta_x = math.asin(math.tan(math.pi/2/3/2))
theta_y = -math.pi/2/2
theta_x = math.atan(1/2)
world.cs(CS().rotate_y(theta_y).rotate_x(theta_x))
if centering:
world.center()
elif event.key == 'shift+left':
world.translate(-0.1*d, 0, 0)
elif event.key == 'shift+right':
world.translate(0.1*d, 0, 0)
elif event.key == 'shift+up':
world.translate(0, 0.1*d, 0)
elif event.key == 'shift+down':
world.translate(0, -0.1*d, 0)
elif event.key == 'ctrl+left':
world.rotate_z(+math.pi/180*10)
elif event.key == 'ctrl+right':
world.rotate_z(-math.pi/180*10)
elif event.key == 'ctrl+up':
world.scale(1.1)
elif event.key == 'ctrl+down':
world.scale(0.9)
for i, j in zip(lps, world.wireframes_xy()):
i.set_data(j)
if event.key == 'h':
visible = h_open.get_visible()
h_open.set_visible(not visible)
h_close.set_visible(visible)
fig.canvas.draw()
def onresize(event, w):
r = 2 * w.space_diagonal()/2
pylab.xlim((-r, r))
pylab.ylim((-r, r))
fig.canvas.mpl_connect('key_press_event',
lambda event: press(event, world, lps,
h_open, h_close))
fig.canvas.mpl_connect('resize_event',
lambda event: onresize(event, world))
pylab.show()