def render(self): w = self.width() n = math.ceil(w / self.unit_length) lc = vp.LineCollection() lc.extend([ 1j * self.dr / 2 + np.linspace( (i + 0.1) * w / n, (i + 0.9) * w / n, int(w / self.quantization)) for i in range(n) ]) lc.extend([ np.array([0.1, 0.4]) * 1j * self.dr + i * w / n for i in range(1, n) ]) lc.extend([ np.array([0.6, 0.9]) * 1j * self.dr + i * w / n for i in range(1, n) ]) lc.extend([ vp.circle(i * w / n, self.dr / 2, DOT_RADIUS, DOT_RADIUS / 15) for i in range(1, n) ]) return self.elem_to_global_lc(lc)
def circle(x: float, y: float, r: float, quantization: float): """Generate lines approximating a circle. The circle is centered on (X, Y) and has a radius of R. """ return vp.LineCollection([vp.circle(x, y, r, quantization)])
def test_snap_no_duplicate(pitch: float): """Snap should return no duplicated points and reject lines that degenerate into a single point.""" lc = execute_single_line(f"snap {pitch}", vp.circle(0, 0, 100, quantization=1)) if len(lc) == 1: assert len(lc[0]) > 1 assert np.all(lc[0][:-1] != lc[0][1:]) else: assert len(lc) == 0
def test_circle_quantization(quantization): line = vp.circle(0, 0, 10, quantization) assert np.max(np.abs(np.diff(line))) < quantization
from vsketch.fill import generate_fill from vsketch.utils import complex_to_2d def _simulate_pen(lc: vp.LineCollection, lw: float) -> MultiPolygon: return unary_union( [ LineString(complex_to_2d(line)).buffer(lw, join_style=2, mitre_limit=10.0) for line in lc ] ) @pytest.mark.skip(reason="this cannot work until Toblerity/Shapely#958 is fixed") @pytest.mark.parametrize("line", [vp.circle(0, 0, 10), vp.rect(0, 0, 10, 20)]) @pytest.mark.parametrize("lw", [0.01, 0.1, 1, 10]) def test_fill(line, lw): """Let's use some computational geometry to ensure the fill pattern properly covers the desired area """ p = Polygon(complex_to_2d(line)) fill_lc = generate_fill(line, lw) overfill_p = _simulate_pen(fill_lc, 1.2 * lw / 2) underfill_p = _simulate_pen(fill_lc, 0.8 * lw / 2) assert overfill_p.contains(p) assert p.contains(underfill_p)
def render_item(self, i, n): return vp.LineCollection( [vp.circle(0, 0, self.unit_length / 4, self.unit_length / 10)])
item_lc = self.render_item(i, n) angle = self.start_angle + (i / n) * (self.stop_angle - self.start_angle) + 90.0 item_lc.rotate(-angle * math.pi / 180.0) item_lc.translate(x, y) lc.extend(item_lc) return lc # noinspection PyMethodMayBeStatic,PyUnusedLocal def render_item(self, i: int, n: int) -> vp.LineCollection: """Render a single item, centered around (0, 0)""" return vp.LineCollection() DOT_RADIUS = 0.01 DOT = vp.circle(0, 0, DOT_RADIUS, DOT_RADIUS) @attr.s class DotElem(SpreadElem): def render_item(self, i, n): return vp.LineCollection([DOT]) @attr.s class CircleElem(SpreadElem): def render_item(self, i, n): return vp.LineCollection( [vp.circle(0, 0, self.unit_length / 4, self.unit_length / 10)])
def circlecrop(lines: vp.LineCollection, x: float, y: float, r: float, quantization: float): """Crop to a circular area.""" circle = Polygon(vp.as_vector(vp.circle(x, y, r, quantization))) mls = lines.as_mls() return vp.LineCollection(mls.intersection(circle))