def make_bundle(vsk, start, base_dir, k, freq, freq2): for t in np.linspace(0, freq2, 200): x = [vp.convert_length(start[0])] y = [vp.convert_length(start[1])] for _ in range(100): dx = k * (vsk.noise(x[-1] * freq, y[-1] * freq, 0 + t) - 0.5) + base_dir[0] dy = k * (vsk.noise(x[-1] * freq, y[-1] * freq, 1000 + t) - 0.5) + base_dir[1] x.append(x[-1] + dx) y.append(y[-1] + dy) vsk.polygon(x, y)
def draw(self, vsk: vsketch.Vsketch) -> None: if self.override_page_size: vsk.size(f"{self.page_width}x{self.page_height}") else: vsk.size(self.page_size, landscape=False) vsk.stroke(1) vsk.fill(1) for i, (y, x) in enumerate( itertools.product(range(self.row_count), range(self.column_count))): pw = self.smallest_width_mm + i * self.width_increment_mm vsk.penWidth(f"{pw}mm", 1) vsk.rect( x * self.horizontal_offset, y * self.vertical_offset, self.box_width, self.box_height, ) vsk.text( f"{pw:.3}mm", x * self.horizontal_offset + self.box_width / 2, y * self.vertical_offset + self.box_height + vpype.convert_length("0.5cm"), mode="label", align="center", size=12, )
def test_stroke_weight_detail(vsk, weight, detail_mm): """We ensure that the desired detail level is respected by strokeWeight""" detail_px = vp.convert_length(str(detail_mm) + "mm") vsk.detail(detail_px) vsk.strokeWeight(weight) # large geometries to filter out polygon's edges # vsk.triangle(0, 0, 160, 0, 80, 160) vsk.square(0, 0, 160) assert line_count_equal(vsk, weight) for line in vsk.document.layers[1]: seg_length = np.abs(np.diff(line)) idx = (seg_length <= detail_px) | (seg_length > 140) assert np.all(idx)
def __init__( self, value: ParamType, min_value: Optional[ParamType] = None, max_value: Optional[ParamType] = None, *, choices: Optional[Sequence[ParamType]] = None, step: Union[None, float, int] = None, unit: str = "", decimals: Optional[int] = None, ): """Create a sketch parameter. This class implements a sketch parameter. Ts automatically recognized by ``vsk`` which generates the corresponding UI in the sketch interactive viewer. :class:`Param` instances must be declared as class member in the :class:`Vsketch` subclass and can then be used using the calling convention:: import vsketch class MySketch(vsketch.Vsketch): page_size = vsketch.Param("a4", choices=["a3", "a4", "a5"]) def draw(self): self.size(self.page_size()) # ... :class:`Param` can encapsulate the following types: :class:`int`, :class:`float`, :class:`str`, and :class:`bool`. For numeral types, a minimum and maximum value may be specified, as well as the step size to use in the UI:: low_bound_param = vsketch.Param(10, 0, step=5) # may not be lower than 0 bounded_param = vsketch.Param(0.5, 0., 1.) # must be within 0.0 and 1.0 For these types, a unit may also be specified:: margin = vsketch.Param(10., unit="mm") In this case, the unit will be displayed in the UI and the value converted to pixel when accessed by the sketch. :class:`float` parameters may further define the number of decimals to display in the UI:: precise_param = vsketch.Param(0.01, decimals=5) Numeral types and string parameters may have a set of possibly choices:: mode = vsketch.Param("simple", choices=["simple", "complex", "versatile"]) """ self.value: ParamType = value self.type = type(value) self.min = self.type( min_value) if min_value is not None else None # type: ignore self.max = self.type( max_value) if max_value is not None else None # type: ignore self.step = step self.decimals = decimals self.unit = unit self.factor: Optional[ float] = None if unit == "" else vp.convert_length(unit) self.choices: Optional[Tuple[ParamType, ...]] = None if choices is not None: self.choices = tuple(self.type(choice) for choice in choices) # type: ignore
def show( document: Document, show_axes: bool, show_grid: bool, show_pen_up: bool, show_points: bool, hide_legend: bool, colorful: bool, unit: str, ): """Display the geometry using matplotlib. By default, only the geometries are displayed without the axis. All geometries are displayed with black. When using the `--colorful` flag, each segment will have a different color (default matplotlib behaviour). This can be useful for debugging purposes. """ # deferred import to optimise startup time import matplotlib.collections import matplotlib.pyplot as plt scale = 1 / convert_length(unit) fig = plt.figure() color_idx = 0 collections = {} # draw page boundaries if document.page_size: w = document.page_size[0] * scale h = document.page_size[1] * scale dw = 10 * scale plt.plot( np.array([0, 1, 1, 0, 0]) * w, np.array([0, 0, 1, 1, 0]) * h, "-k", lw=0.25, label=None, ) plt.fill( np.array([w, w + dw, w + dw, dw, dw, w]), np.array([dw, dw, h + dw, h + dw, h, h]), "k", alpha=0.3, label=None, ) for layer_id, lc in document.layers.items(): if colorful: color = COLORS[color_idx:] + COLORS[:color_idx] marker_color = "k" color_idx += len(lc) else: color = COLORS[color_idx] # type: ignore marker_color = [color] # type: ignore color_idx += 1 if color_idx >= len(COLORS): color_idx = color_idx % len(COLORS) layer_lines = matplotlib.collections.LineCollection( (as_vector(line) * scale for line in lc), color=color, lw=1, alpha=0.5, label=str(layer_id), ) collections[layer_id] = [layer_lines] plt.gca().add_collection(layer_lines) if show_points: points = np.hstack([line for line in lc]) * scale layer_points = plt.gca().scatter(points.real, points.imag, marker=".", c=marker_color, s=16) collections[layer_id].append(layer_points) if show_pen_up: pen_up_lines = matplotlib.collections.LineCollection( ((as_vector(lc[i])[-1] * scale, as_vector(lc[i + 1])[0] * scale) for i in range(len(lc) - 1)), color=(0, 0, 0), lw=0.5, alpha=0.5, ) collections[layer_id].append(pen_up_lines) plt.gca().add_collection(pen_up_lines) plt.gca().invert_yaxis() plt.axis("equal") plt.margins(0, 0) if not hide_legend: lgd = plt.legend(loc="upper right") # we will set up a dict mapping legend line to orig line, and enable # picking on the legend line line_dict = {} for lgd_line, lgd_text in zip(lgd.get_lines(), lgd.get_texts()): lgd_line.set_picker(True) # 5 pts tolerance lgd_line.set_pickradius(5) layer_id = int(lgd_text.get_text()) if layer_id in collections: line_dict[lgd_line] = collections[layer_id] def on_pick(event): line = event.artist vis = not line_dict[line][0].get_visible() for ln in line_dict[line]: ln.set_visible(vis) if vis: line.set_alpha(1.0) else: line.set_alpha(0.2) fig.canvas.draw() fig.canvas.mpl_connect("pick_event", on_pick) if show_axes or show_grid: plt.axis("on") plt.xlabel(f"[{unit}]") plt.ylabel(f"[{unit}]") else: plt.axis("off") if show_grid: plt.grid("on") plt.show() return document
def display_matplotlib( document: vp.Document, page_size: Tuple[float, float] = None, center: bool = False, show_axes: bool = True, show_grid: bool = False, show_pen_up: bool = False, colorful: bool = False, unit: str = "px", fig_size: Tuple[float, float] = None, ) -> None: scale = 1 / vp.convert_length(unit) if fig_size: plt.figure(figsize=fig_size) plt.cla() # draw page if page_size is not None: w = page_size[0] * scale h = page_size[1] * scale dw = 10 * scale plt.fill( np.array([w, w + dw, w + dw, dw, dw, w]), np.array([dw, dw, h + dw, h + dw, h, h]), "k", alpha=0.3, ) plt.plot(np.array([0, 1, 1, 0, 0]) * w, np.array([0, 0, 1, 1, 0]) * h, "-k", lw=0.25) # compute offset offset = complex(0, 0) if center and page_size: bounds = document.bounds() if bounds is not None: offset = complex( (page_size[0] - (bounds[2] - bounds[0])) / 2.0 - bounds[0], (page_size[1] - (bounds[3] - bounds[1])) / 2.0 - bounds[1], ) offset_ndarr = np.array([offset.real, offset.imag]) # plot all layers color_idx = 0 collections = {} for layer_id, lc in document.layers.items(): if colorful: color: Union[Tuple[float, float, float], List[Tuple[float, float, float]]] = (COLORS[color_idx:] + COLORS[:color_idx]) color_idx += len(lc) else: color = COLORS[color_idx] color_idx += 1 if color_idx >= len(COLORS): color_idx = color_idx % len(COLORS) # noinspection PyUnresolvedReferences layer_lines = matplotlib.collections.LineCollection( (vp.as_vector(line + offset) * scale for line in lc), color=color, lw=1, alpha=0.5, label=str(layer_id), ) collections[layer_id] = [layer_lines] plt.gca().add_collection(layer_lines) if show_pen_up: # noinspection PyUnresolvedReferences pen_up_lines = matplotlib.collections.LineCollection( (( (vp.as_vector(lc[i])[-1] + offset_ndarr) * scale, (vp.as_vector(lc[i + 1])[0] + offset_ndarr) * scale, ) for i in range(len(lc) - 1)), color=(0, 0, 0), lw=0.5, alpha=0.5, ) collections[layer_id].append(pen_up_lines) plt.gca().add_collection(pen_up_lines) plt.gca().invert_yaxis() plt.axis("equal") plt.margins(0, 0) if show_axes or show_grid: plt.axis("on") plt.xlabel(f"[{unit}]") plt.ylabel(f"[{unit}]") else: plt.axis("off") if show_grid: plt.grid("on") plt.show()