Alexandre B A Villares 2022-06-18 20:57:12 -03:00
rodzic 42715432c7
commit c627188818
5 zmienionych plików z 521 dodań i 0 usunięć

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.0 MiB

Wyświetl plik

@ -0,0 +1,251 @@
# unnecessary when importing from villares.arc.py module
beginShape = begin_shape
endShape = end_shape
bezierVertex = bezier_vertex
textSize = text_size
DEBUG, TEXT_HEIGHT = False, 12
from random import sample
from itertools import product
from villares.helpers import lerp_tuple
def setup():
global input_pts
size(600, 600, P3D)
input_pts = set_target()
def draw():
global input_pts
background(0, 0, 100)
translate(width / 2, height / 2, -height / 2)
rotate_y(frame_count / 100.0)
translate(-width / 2, -height / 2, 225)
input_pts = lerp_tuple(input_pts, target_pts, 0.1)
for i in range(10):
output_pts = arc_augmented_points(
input_pts,
radius=50 + i * 10,
auto_flip=True,
gradual_flip=True,
min_dist=5,
)
no_fill()
stroke(200)
stroke_weight(2)
with begin_closed_shape():
vertices(output_pts)
translate(0, 0, -20)
stroke_weight(5)
stroke(250, 100, 0)
points(output_pts)
def key_pressed():
set_target()
def set_target():
global target_pts
grid = list(product(range(100, 600, 100),
repeat=2))
pts = sample(grid, 5)
target_pts = pts
return pts
def arc_augmented_points(op_list, or_list=None, **kwargs):
"""
A version of arc_augmented_poly that returns the points
of a poly-approximation with arc_pts
"""
def mid(p0, p1):
return (p0[0] + p1[0]) * 0.5, (p0[1] + p1[1]) * 0.5
assert op_list, 'No points were provided.'
assert not (
'radius' in kwargs and or_list
), "You can't use a radii list and a radius kwarg together."
if 'radius' in kwargs and or_list == None:
or_list = [kwargs.pop('radius')] * len(op_list)
r2_list = list(or_list)
assert len(op_list) == len(
r2_list
), 'Number of points and radii provided not the same.'
auto_flip = kwargs.pop('auto_flip', True)
gradual_flip = kwargs.pop('gradual_flip', False) # experimentas
pts_list = []
# remove overlapping adjacent points
p_list, r_list = [], []
p2_list = list(op_list)
for i1, p1 in reversed(list(enumerate(p2_list))):
i2 = (i1 + 1) % len(p2_list)
p2, r2, r1 = p2_list[i2], r2_list[i2], r2_list[i1]
d = dist(p1[0], p1[1], p2[0], p2[1])
if d > abs(r1 - r2):
p_list.append(p1)
r_list.append(r1)
else:
p2_list[i2] = mid(p1, p2)
r2_list[i2] = max(r1, r2)
# invert radius
for i1, p1 in enumerate(p_list):
i0 = i1 - 1
p0 = p_list[i0]
i2 = (i1 + 1) % len(p_list)
p2, r2, r1, r0 = p_list[i2], r_list[i2], r_list[i1], r_list[i0]
cct0 = circ_circ_tangent(p0, p1, r0, r1)
cct1 = circ_circ_tangent(p1, p2, r1, r2)
ang0, a0, b0 = cct0
ang1, a1, b1 = cct1
a = triangle_area(a0, b0, b1) / 1000
if auto_flip and simple_intersect(a0, b0, a1, b1):
r_list[i1] = -r_list[i1]
# if auto_flip and a < 0:
# r_list[i1] = -r_list[i1]
if gradual_flip:
r_list[i1] = r_list[i1] * min(1, abs(a))
# reduce radius that won't fit
for i1, p1 in enumerate(p_list):
i2 = (i1 + 1) % len(p_list)
p2, r2, r1 = p_list[i2], r_list[i2], r_list[i1]
r_list[i1], r_list[i2] = reduce_radius(p1, p2, r1, r2)
# calculate the tangents
a_list = []
for i1, p1 in enumerate(p_list):
i2 = (i1 + 1) % len(p_list)
p2, r2, r1 = p_list[i2], r_list[i2], r_list[i1]
cct = circ_circ_tangent(p1, p2, r1, r2)
a_list.append(cct)
if DEBUG:
push()
ang, (xa, ya), (xb, yb) = cct
stroke(255, 0, 0)
stroke_weight(5)
line(xa, ya, xb, yb)
pop()
# now draw it!
for i1, ia in enumerate(a_list):
i2 = (i1 + 1) % len(a_list)
p1, p2, r1, r2 = p_list[i1], p_list[i2], r_list[i1], r_list[i2]
a1, p11, p12 = ia
a2, p21, p22 = a_list[i2]
if DEBUG:
circle(p1[0], p1[1], r1 * 2)
if a1 != None and a2 != None:
start = a1 if a1 < a2 else a1 - TWO_PI # was <
if r2 < 0: # was <=
a2 = a2 - TWO_PI
abs_angle = abs(a2 - start)
if abs_angle > TWO_PI:
if a2 < 0:
a2 += TWO_PI
else:
a2 -= TWO_PI
if abs(a2 - start) != TWO_PI:
pts_list.extend(
arc_pts(p2[0], p2[1], r2 * 2, r2 * 2, start, a2, **kwargs)
)
if DEBUG:
textSize(TEXT_HEIGHT)
text(
' {:0.2f} {:0.2f}'.format(r2, degrees(abs_angle)),
p2[0],
p2[1],
)
else:
# when the the segment is smaller than the diference between
# radius, circ_circ_tangent won't renturn the angle
if DEBUG:
ellipse(p2[0], p2[1], r1, r1)
if a1:
pts_list.append((p12[0], p12[1]))
if a2:
pts_list.append((p21[0], p21[1]))
return pts_list
def reduce_radius(p1, p2, r1, r2, reduce_both=True):
d = dist(p1[0], p1[1], p2[0], p2[1])
ri = abs(r1 - r2)
if d - ri <= 0:
if reduce_both:
r1, r2 = (
remap(d, ri + 1, 0, r1, (r1 + r2) / 2),
remap(d, ri + 1, 0, r2, (r1 + r2) / 2),
)
elif abs(r1) > abs(r2):
r1 = remap(d, ri + 1, 0, r1, r2)
else:
r2 = remap(d, ri + 1, 0, r2, r1)
return r1, r2
def circ_circ_tangent(p1, p2, r1, r2):
d = dist(p1[0], p1[1], p2[0], p2[1])
ri = r1 - r2
line_angle = atan2(p1[0] - p2[0], p2[1] - p1[1])
if d - abs(ri) >= 0:
theta = asin(ri / float(d))
x1 = -cos(line_angle + theta) * r1
y1 = -sin(line_angle + theta) * r1
x2 = -cos(line_angle + theta) * r2
y2 = -sin(line_angle + theta) * r2
return (
line_angle + theta,
(p1[0] - x1, p1[1] - y1),
(p2[0] - x2, p2[1] - y2),
)
else:
return (None, (p1[0], p1[1]), (p2[0], p2[1]))
def triangle_area(a, b, c):
area = a[0] * (b[1] - c[1]) + b[0] * (c[1] - a[1]) + c[0] * (a[1] - b[1])
return area
def arc_pts(cx, cy, w, h, start_angle, end_angle, num_points=24, min_dist=0):
"""
Returns points approximating an arc using the same
signature as the original Processing arc().
"""
sweep_angle = end_angle - start_angle
if abs(sweep_angle) < 0.0001:
vx = cx + cos(start_angle) * w / 2.0
vy = cy + sin(start_angle) * h / 2.0
return [(vx, vy)]
pts_list = []
step_angle = float(sweep_angle) / num_points
va = start_angle
side = 1 if sweep_angle > 0 else -1
while va * side < end_angle * side or abs(va - end_angle) < 0.0001:
vx = cx + cos(va) * w / 2.0
vy = cy + sin(va) * h / 2.0
pts_list.append((vx, vy))
va += step_angle
if not min_dist or len(pts_list) < 3:
return pts_list
else:
# def simplify_pts(pts_list, min_dist):
previous_x, previous_y = pts_list[0]
pts_simplified = [pts_list[0]]
for x, y in pts_list[1:]:
if dist(x, y, previous_x, previous_y) >= min_dist:
pts_simplified.append((x, y))
previous_x, previous_y = x, y
return pts_simplified
def ccw(a, b, c):
"""Returns True if the points are arranged counter-clockwise in the plane"""
return (b[0] - a[0]) * (c[1] - a[1]) > (b[1] - a[1]) * (c[0] - a[0])
def simple_intersect(a1, b1, a2, b2):
"""Returns True if line segments intersect."""
return ccw(a1, b1, a2) != ccw(a1, b1, b2) and ccw(a2, b2, a1) != ccw(
a2, b2, b1
)

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 61 KiB

Wyświetl plik

@ -0,0 +1,254 @@
# unnecessary when importing from villares.arc.py module
beginShape = begin_shape
endShape = end_shape
bezierVertex = bezier_vertex
textSize = text_size
DEBUG, TEXT_HEIGHT = False, 12
from random import sample, seed
from itertools import product
from villares.helpers import lerp_tuple
def setup():
global input_pts
size(600, 600, P3D)
input_pts = set_target()
def draw():
global input_pts
background(50, 0, 100)
translate(width / 2, height / 2, -height * 1.5)
rotate_y(frame_count / 100.0)
translate(-width / 2, -height / 2, 120)
input_pts = lerp_tuple(input_pts, target_pts, 0.1)
for i in range(16):
output_pts = arc_augmented_points(
input_pts,
radius=100 + i * 15,
auto_flip=True,
gradual_flip=True,
min_dist=5,
)
no_fill()
stroke(200)
stroke_weight(2)
with begin_closed_shape():
vertices(output_pts)
translate(0, 0, -20)
push()
translate(0, 0, 10)
stroke_weight(5)
stroke(0, 100, 250)
points(output_pts)
pop()
def key_pressed():
set_target()
def set_target():
global target_pts
grid = list(product(range(100, 600, 200),
repeat=2))
pts = sample(grid, 5)
target_pts = pts
return pts
def arc_augmented_points(op_list, or_list=None, **kwargs):
"""
A version of arc_augmented_poly that returns the points
of a poly-approximation with arc_pts
"""
def mid(p0, p1):
return (p0[0] + p1[0]) * 0.5, (p0[1] + p1[1]) * 0.5
assert op_list, 'No points were provided.'
assert not (
'radius' in kwargs and or_list
), "You can't use a radii list and a radius kwarg together."
if 'radius' in kwargs and or_list == None:
or_list = [kwargs.pop('radius')] * len(op_list)
r2_list = list(or_list)
assert len(op_list) == len(
r2_list
), 'Number of points and radii provided not the same.'
auto_flip = kwargs.pop('auto_flip', True)
gradual_flip = kwargs.pop('gradual_flip', False) # experimentas
pts_list = []
# remove overlapping adjacent points
p_list, r_list = [], []
p2_list = list(op_list)
for i1, p1 in reversed(list(enumerate(p2_list))):
i2 = (i1 + 1) % len(p2_list)
p2, r2, r1 = p2_list[i2], r2_list[i2], r2_list[i1]
d = dist(p1[0], p1[1], p2[0], p2[1])
if d > abs(r1 - r2):
p_list.append(p1)
r_list.append(r1)
else:
p2_list[i2] = mid(p1, p2)
r2_list[i2] = max(r1, r2)
# invert radius
for i1, p1 in enumerate(p_list):
i0 = i1 - 1
p0 = p_list[i0]
i2 = (i1 + 1) % len(p_list)
p2, r2, r1, r0 = p_list[i2], r_list[i2], r_list[i1], r_list[i0]
cct0 = circ_circ_tangent(p0, p1, r0, r1)
cct1 = circ_circ_tangent(p1, p2, r1, r2)
ang0, a0, b0 = cct0
ang1, a1, b1 = cct1
a = triangle_area(a0, b0, b1) / 1000
if auto_flip and simple_intersect(a0, b0, a1, b1):
r_list[i1] = -r_list[i1]
# if auto_flip and a < 0:
# r_list[i1] = -r_list[i1]
if gradual_flip:
r_list[i1] = r_list[i1] * min(1, abs(a))
# reduce radius that won't fit
for i1, p1 in enumerate(p_list):
i2 = (i1 + 1) % len(p_list)
p2, r2, r1 = p_list[i2], r_list[i2], r_list[i1]
r_list[i1], r_list[i2] = reduce_radius(p1, p2, r1, r2)
# calculate the tangents
a_list = []
for i1, p1 in enumerate(p_list):
i2 = (i1 + 1) % len(p_list)
p2, r2, r1 = p_list[i2], r_list[i2], r_list[i1]
cct = circ_circ_tangent(p1, p2, r1, r2)
a_list.append(cct)
if DEBUG:
push()
ang, (xa, ya), (xb, yb) = cct
stroke(255, 0, 0)
stroke_weight(5)
line(xa, ya, xb, yb)
pop()
# now draw it!
for i1, ia in enumerate(a_list):
i2 = (i1 + 1) % len(a_list)
p1, p2, r1, r2 = p_list[i1], p_list[i2], r_list[i1], r_list[i2]
a1, p11, p12 = ia
a2, p21, p22 = a_list[i2]
if DEBUG:
circle(p1[0], p1[1], r1 * 2)
if a1 != None and a2 != None:
start = a1 if a1 < a2 else a1 - TWO_PI # was <
if r2 < 0: # was <=
a2 = a2 - TWO_PI
abs_angle = abs(a2 - start)
if abs_angle > TWO_PI:
if a2 < 0:
a2 += TWO_PI
else:
a2 -= TWO_PI
if abs(a2 - start) != TWO_PI:
pts_list.extend(
arc_pts(p2[0], p2[1], r2 * 2, r2 * 2, start, a2, **kwargs)
)
if DEBUG:
textSize(TEXT_HEIGHT)
text(
' {:0.2f} {:0.2f}'.format(r2, degrees(abs_angle)),
p2[0],
p2[1],
)
else:
# when the the segment is smaller than the diference between
# radius, circ_circ_tangent won't renturn the angle
if DEBUG:
ellipse(p2[0], p2[1], r1, r1)
if a1:
pts_list.append((p12[0], p12[1]))
if a2:
pts_list.append((p21[0], p21[1]))
return pts_list
def reduce_radius(p1, p2, r1, r2, reduce_both=True):
d = dist(p1[0], p1[1], p2[0], p2[1])
ri = abs(r1 - r2)
if d - ri <= 0:
if reduce_both:
r1, r2 = (
remap(d, ri + 1, 0, r1, (r1 + r2) / 2),
remap(d, ri + 1, 0, r2, (r1 + r2) / 2),
)
elif abs(r1) > abs(r2):
r1 = remap(d, ri + 1, 0, r1, r2)
else:
r2 = remap(d, ri + 1, 0, r2, r1)
return r1, r2
def circ_circ_tangent(p1, p2, r1, r2):
d = dist(p1[0], p1[1], p2[0], p2[1])
ri = r1 - r2
line_angle = atan2(p1[0] - p2[0], p2[1] - p1[1])
if d - abs(ri) >= 0:
theta = asin(ri / float(d))
x1 = -cos(line_angle + theta) * r1
y1 = -sin(line_angle + theta) * r1
x2 = -cos(line_angle + theta) * r2
y2 = -sin(line_angle + theta) * r2
return (
line_angle + theta,
(p1[0] - x1, p1[1] - y1),
(p2[0] - x2, p2[1] - y2),
)
else:
return (None, (p1[0], p1[1]), (p2[0], p2[1]))
def triangle_area(a, b, c):
area = a[0] * (b[1] - c[1]) + b[0] * (c[1] - a[1]) + c[0] * (a[1] - b[1])
return area
def arc_pts(cx, cy, w, h, start_angle, end_angle, num_points=24, min_dist=0):
"""
Returns points approximating an arc using the same
signature as the original Processing arc().
"""
sweep_angle = end_angle - start_angle
if abs(sweep_angle) < 0.0001:
vx = cx + cos(start_angle) * w / 2.0
vy = cy + sin(start_angle) * h / 2.0
return [(vx, vy)]
pts_list = []
step_angle = float(sweep_angle) / num_points
va = start_angle
side = 1 if sweep_angle > 0 else -1
while va * side < end_angle * side or abs(va - end_angle) < 0.0001:
vx = cx + cos(va) * w / 2.0
vy = cy + sin(va) * h / 2.0
pts_list.append((vx, vy))
va += step_angle
if not min_dist or len(pts_list) < 3:
return pts_list
else:
# def simplify_pts(pts_list, min_dist):
previous_x, previous_y = pts_list[0]
pts_simplified = [pts_list[0]]
for x, y in pts_list[1:]:
if dist(x, y, previous_x, previous_y) >= min_dist:
pts_simplified.append((x, y))
previous_x, previous_y = x, y
return pts_simplified
def ccw(a, b, c):
"""Returns True if the points are arranged counter-clockwise in the plane"""
return (b[0] - a[0]) * (c[1] - a[1]) > (b[1] - a[1]) * (c[0] - a[0])
def simple_intersect(a1, b1, a2, b2):
"""Returns True if line segments intersect."""
return ccw(a1, b1, a2) != ccw(a1, b1, b2) and ccw(a2, b2, a1) != ccw(
a2, b2, b1
)

Wyświetl plik

@ -27,6 +27,22 @@ Here are listed some of the tools I have been using:
---
### sketch_2022_06_18
![sketch_2022_06_18](2022/sketch_2022_06_18/sketch_2022_06_18.png)
[sketch_2022_06_18](https://github.com/villares/sketch-a-day/tree/master/2022/sketch_2022_06_18) [[py5](https://py5.ixora.io/)]
---
### sketch_2022_06_17
![sketch_2022_06_17](2022/sketch_2022_06_17/sketch_2022_06_17.gif)
[sketch_2022_06_17](https://github.com/villares/sketch-a-day/tree/master/2022/sketch_2022_06_17) [[py5](https://py5.ixora.io/)]
---
### sketch_2022_06_16
![sketch_2022_06_16](2022/sketch_2022_06_16/sketch_2022_06_16.gif)