# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # 第28课.自定义符号散点图
# > Creating a custom ellipse symbol in scatter plot.
#
# 在散点图中自定义椭圆形符号。这是一个看似简单，又不简单的例子，很好。

# \[1]:

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

# \[2]: 准备中文，调整显示方式

# %matplotlib widget
matplotlib.rcParams['font.family'] = ['DengXian', 'sans-serif']

# \[3]: 准备数据

rx, ry = 3., 1.
area = rx * ry * np.pi
theta = np.arange(0, 2 * np.pi + 0.01, 0.1)
verts = np.column_stack([rx / area * np.cos(theta), ry / area * np.sin(theta)])

# 椭圆的参数方程: x=acosθ y=bsinθ

x, y, s, c = np.random.rand(4, 30)
s *= 10**2.

# \[4]: 准备图与绘图区

# + jupyter={"outputs_hidden": true}
fig, ax = plt.subplots()
# -

# \[5]: 绘制散点图

ax.scatter(x, y, s, c, marker=verts)

# 完成，收工。
#
# 这样看起来这确实是一个简单的例子，但是与*第34课.打标散点图*对比，就能看出不简单的地方。在第34课中讲过marker参数描述标记样式。标记可以是MarkerStyle类的实例，也可以是特定标记的文本简写。缺省为None，此时使用rcParams["scatter.marker"]值，缺省是'o'。
#
# 但在上面的例子中verts既不是文本简写，也不是MarkerStyle类的实例，这是怎么回事呢？



isinstance(verts, matplotlib.markers.MarkerStyle)

# 查阅scatter方法的定义
# ```python
# def scatter(self, x, y, s=None, c=None, marker=None ...):
#     # load default marker from rcParams
#     if marker is None:
#         marker = rcParams['scatter.marker']
#
#     if isinstance(marker, mmarkers.MarkerStyle):
#         marker_obj = marker
#     else:
#         marker_obj = mmarkers.MarkerStyle(marker)
# ```
# 可以看到verts作为MarkerStyle构造器的参数实例化marker对象。
#
# MarkerStyle是markers模块的类，模块包含处理标记的功能，被plot和scatter方法的标记功能使用。
#
# > A list of (x, y) pairs used for Path vertices. The center of the marker is located at (0, 0) and the size is normalized, such that the created path is encapsulated inside the unit cell.
#
# 在marker所有可能的定义中，有一种定义是一个列表，元素为(x, y)对，被用于路径Path顶点（路点）。标记的中心定位在(0, 0)，大小是标准化后的尺寸，以便将创建的路径封装在单位单元内。

# ### 实验1 观察verts变量及其对应的marker_obj对象

verts.shape

verts[0:5,:]

marker_obj = matplotlib.markers.MarkerStyle(verts)
path = marker_obj.get_path()

# 缺省的路径指令是，MOVETO,LINETO...

# - 椭圆的面积是 πab
# - 椭圆上任意一点到F1，F2距离的和为2a，F1，F2之间的距离为2c。而公式中的b²=a²-c²。b是为了书写方便设定的参数。
# - verts 是一个椭圆的路径点

# ### 实验2 绘制marker_obj对象

fig, ax = plt.subplots()

patch = matplotlib.patches.PathPatch(path, facecolor='red', lw=2)
ax.add_patch(patch)
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)

# ### 实验3 
# path = marker_obj.get_path().transformed(
#             marker_obj.get_transform())

ax.cla()

path = marker_obj.get_path().transformed(
            marker_obj.get_transform())
patch = matplotlib.patches.PathPatch(path, facecolor='orange', lw=2)
ax.add_patch(patch)
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)

# #### transform
# **Ref**
# - [二维仿射几何变换](https://ww2.mathworks.cn/help/images/ref/affine2d.html)
# - [Affine transform of an image](https://matplotlib.org/3.1.3/gallery/images_contours_and_fields/affine_image.html)
# - [何为仿射变换(Affine Transformation)](https://www.cnblogs.com/bnuvincent/p/6691189.html)
# - [椭圆参数方程](https://jingyan.baidu.com/article/22a299b5c193a99e19376abe.html)
# - [仿射变换及其变换矩阵的理解](https://www.cnblogs.com/shine-lee/p/10950963.html)
# - 线性变换
# - [matplotlib.transforms](https://matplotlib.org/api/transformations.html)
# - [Transformations Tutorial](https://matplotlib.org/tutorials/advanced/transforms_tutorial.html#sphx-glr-tutorials-advanced-transforms-tutorial-py)
# > from .transforms import IdentityTransform, Affine2D
#
#     def get_transform(self):
#         return self._transform.frozen()
#         

t = marker_obj.get_transform()

# +
# t?
# -

# ## path
# transformed?
# ```python
#     def transformed(self, transform):
#         """
#         Return a transformed copy of the path.
#
#         See Also
#         --------
#         matplotlib.transforms.TransformedPath
#             A specialized path class that will cache the transformed result and
#             automatically update when the transform changes.
#         """
#         return Path(transform.transform(self.vertices), self.codes,
#                     self._interpolation_steps)
# ```

# +
# path._interpolation_steps??
# -

marker_obj.get_transform()

# + jupyter={"outputs_hidden": true}
t.transform(path.vertices)
# -

# 放大的仿射变换
t.get_matrix()

t.identity().get_matrix()

marker_obj.get_transform()

# 这是怎么回事？
# ```python
#         # load default marker from rcParams
#         if marker is None:
#             marker = rcParams['scatter.marker']
#
#         if isinstance(marker, mmarkers.MarkerStyle):
#             marker_obj = marker
#         else:
#             marker_obj = mmarkers.MarkerStyle(marker)
#
#         path = marker_obj.get_path().transformed(
#             marker_obj.get_transform())
#         if not marker_obj.is_filled():
#             edgecolors = 'face'
#             linewidths = rcParams['lines.linewidth']
#
#         offsets = np.ma.column_stack([x, y])
#
#         collection = mcoll.PathCollection(
#                 (path,), scales,
#                 facecolors=colors,
#                 edgecolors=edgecolors,
#                 linewidths=linewidths,
#                 offsets=offsets,
#                 transOffset=kwargs.pop('transform', self.transData),
#                 alpha=alpha
#                 )
#         collection.set_transform(mtransforms.IdentityTransform())
#         collection.update(kwargs)
#
#         if colors is None:
#             collection.set_array(c)
#             collection.set_cmap(cmap)
#             collection.set_norm(norm)
#
#             if vmin is not None or vmax is not None:
#                 collection.set_clim(vmin, vmax)
#             else:
#                 collection.autoscale_None()
#                 
#         self.add_collection(collection)
# ```
# > marker_obj = mmarkers.MarkerStyle(marker)
# verts作为MarkerStyle构造器的参数，实例化marker对象

# path没有路径指令？

# 默认的路径指令可能是，MOVETO,LINETO...,CLOSEPOLY



# + jupyter={"outputs_hidden": true}
np.ma.column_stack([x, y])
# -

matplotlib.transforms.IdentityTransform().get_matrix()

# collection = mcoll.PathCollection(
#                 (path,), scales,
#                 facecolors=colors,
#                 edgecolors=edgecolors,
#                 linewidths=linewidths,
#                 offsets=offsets,
#                 transOffset=kwargs.pop('transform', self.transData),
#                 alpha=alpha
#                 )

# ### matplotlib.collections
#
# > Classes for the efficient drawing of large collections of objects that share most properties, e.g., a large number of line segments or polygons.
#
# > The classes are not meant to be as flexible as their single element counterparts (e.g., you may not be able to select all line styles) but they are meant to be fast for common use cases (e.g., a large set of solid line segments).
#
# 模块中包含的类用于高效绘制大量共享属性的对象，如大量的线段或多边形。
#
# 这些类并不像它们的单元素对应物那样灵活（例如，您可能无法选择所有线型），但是对于常见用例（例如，大量的实线线段）而言，它们的作用就是快。

# #### class matplotlib.collections.PathCollection(paths, sizes=None, **kwargs)
#
# > This is the most basic Collection subclass. A PathCollection is e.g. created by a scatter() plot.
#
# 这是最基本的Collection子类。PathCollection由制图方法创建。例如 由scatter（）
#
# > paths is a sequence of matplotlib.path.Path instances.
#
# 参数paths是Path实例的序列。
#
#     Valid Collection keyword arguments:
#
#             edgecolors: None
#             facecolors: None
#             linewidths: None
#             antialiaseds: None
#             offsets: None
#             transOffset: transforms.IdentityTransform()
#             norm: None (optional for matplotlib.cm.ScalarMappable)
#             cmap: None (optional for matplotlib.cm.ScalarMappable)
#
#     offsets and transOffset are used to translate the patch after rendering (default no offsets)
#
#     If any of edgecolors, facecolors, linewidths, antialiaseds are None, they default to their matplotlib.rcParams patch setting, in sequence form.
#

fig_t, ax_t = plt.subplots()

x, y = np.random.rand(2, 30)

marker_obj = matplotlib.markers.MarkerStyle('+')

path = marker_obj.get_path()

offsets = np.ma.column_stack([x, y])

collection = matplotlib.collections.PathCollection((path,), 100,offsets=offsets, transOffset=ax_t.transData)

ax_t.add_collection(collection)



StarcoderdataJupyterScriptsDedupFiltered