villares 2019-06-10 17:03:38 -03:00
rodzic 0403759647
commit f3d8ddb362
11 zmienionych plików z 598 dodań i 1 usunięć

Plik binarny nie jest wyświetlany.

Po

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

Plik binarny nie jest wyświetlany.

Po

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

Plik binarny nie jest wyświetlany.

Po

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

Wyświetl plik

@ -1,3 +1,4 @@
from copy import deepcopy
from arcs import *
class Poly():

Wyświetl plik

@ -4,7 +4,6 @@ A minimal poly editor
- Add points
"""
import pickle
from copy import deepcopy
from poly import Poly
# add_library('GifAnimation')
# from gif_exporter import gif_export

Wyświetl plik

@ -0,0 +1,153 @@
def b_poly_filleted(p_list, r_list=None, open_poly=False):
"""
draws a 'filleted' polygon with variable radius
dependent on roundedCorner()
"""
if not r_list:
r_list = [0] * len(p_list)
assert len(p_list) == len(r_list), \
"Number of points and radii not the same"
strokeJoin(ROUND)
beginShape()
for p0, p1, p2, r in zip(p_list,
[p_list[-1]] + p_list[:-1],
[p_list[-2]] + [p_list[-1]] + p_list[:-2],
[r_list[-1]] + r_list[:-1]
):
m1 = (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2
m2 = (p2[0] + p1[0]) / 2, (p2[1] + p1[1]) / 2
b_roundedCorner(p1, m1, m2, r)
endShape(CLOSE)
def b_roundedCorner(pc, p2, p1, r):
"""
Based on Stackoverflow C# rounded corner post
https://stackoverflow.com/questions/24771828/algorithm-for-creating-rounded-corners-in-a-polygon
"""
def GetProportionPoint(pt, segment, L, dx, dy):
factor = float(segment) / L if L != 0 else segment
return PVector((pt[0] - dx * factor), (pt[1] - dy * factor))
# Vector 1
dx1 = pc[0] - p1[0]
dy1 = pc[1] - p1[1]
# Vector 2
dx2 = pc[0] - p2[0]
dy2 = pc[1] - p2[1]
# Angle between vector 1 and vector 2 divided by 2
angle = (atan2(dy1, dx1) - atan2(dy2, dx2)) / 2
# The length of segment between angular point and the
# points of intersection with the circle of a given radius
tng = abs(tan(angle))
segment = r / tng if tng != 0 else r
# Check the segment
length1 = sqrt(dx1 * dx1 + dy1 * dy1)
length2 = sqrt(dx2 * dx2 + dy2 * dy2)
min_len = min(length1, length2)
if segment > min_len:
segment = min_len
max_r = min_len * abs(tan(angle))
else:
max_r = r
# Points of intersection are calculated by the proportion between
# length of vector and the length of the segment.
p1Cross = GetProportionPoint(pc, segment, length1, dx1, dy1)
p2Cross = GetProportionPoint(pc, segment, length2, dx2, dy2)
# Calculation of the coordinates of the circle
# center by the addition of angular vectors.
dx = pc[0] * 2 - p1Cross[0] - p2Cross[0]
dy = pc[1] * 2 - p1Cross[1] - p2Cross[1]
L = sqrt(dx * dx + dy * dy)
d = sqrt(segment * segment + max_r * max_r)
circlePoint = GetProportionPoint(pc, d, L, dx, dy)
# StartAngle and EndAngle of arc
startAngle = atan2(p1Cross[1] - circlePoint[1],
p1Cross[0] - circlePoint[0])
endAngle = atan2(p2Cross[1] - circlePoint[1],
p2Cross[0] - circlePoint[0])
# Sweep angle
sweepAngle = endAngle - startAngle
# Some additional checks
A, B = False, False
if sweepAngle < 0:
A = True
startAngle, endAngle = endAngle, startAngle
sweepAngle = -sweepAngle
# ellipse(pc[0], pc[1], 15, 15) # debug
if sweepAngle > PI:
B = True
startAngle, endAngle = endAngle, startAngle
sweepAngle = TWO_PI - sweepAngle
# ellipse(pc[0], pc[1], 25, 25) # debug
if (A and not B) or (B and not A):
startAngle, endAngle = endAngle, startAngle
sweepAngle = -sweepAngle
# ellipse(pc[0], pc[1], 5, 5) # debug
b_arc(circlePoint[0], circlePoint[1], 2 * max_r, 2 * max_r,
startAngle, startAngle + sweepAngle, arc_type=2)
def b_arc(cx, cy, w, h, startAngle, endAngle, arc_type=0):
"""
A bezier approximation of an arc
using the same signature as the original Processing arc()
arc_type: 0 "normal" arc, using beginShape() and endShape()
1 "middle" used in recursive call of smaller arcs
2 "naked" like normal, but without beginShape() and endShape()
for use inside a larger PShape
"""
theta = endAngle - startAngle
# Compute raw Bezier coordinates.
if arc_type != 1 or theta < HALF_PI:
x0 = cos(theta / 2.0)
y0 = sin(theta / 2.0)
x3 = x0
y3 = 0 - y0
x1 = (4.0 - x0) / 3.0
if y0 != 0:
y1 = ((1.0 - x0) * (3.0 - x0)) / (3.0 * y0) # y0 != 0...
else:
y1 = 0
x2 = x1
y2 = 0 - y1
# Compute rotationally-offset Bezier coordinates, using:
# x' = cos(angle) * x - sin(angle) * y
# y' = sin(angle) * x + cos(angle) * y
bezAng = startAngle + theta / 2.0
cBezAng = cos(bezAng)
sBezAng = sin(bezAng)
rx0 = cBezAng * x0 - sBezAng * y0
ry0 = sBezAng * x0 + cBezAng * y0
rx1 = cBezAng * x1 - sBezAng * y1
ry1 = sBezAng * x1 + cBezAng * y1
rx2 = cBezAng * x2 - sBezAng * y2
ry2 = sBezAng * x2 + cBezAng * y2
rx3 = cBezAng * x3 - sBezAng * y3
ry3 = sBezAng * x3 + cBezAng * y3
# Compute scaled and translated Bezier coordinates.
rx, ry = w / 2.0, h / 2.0
px0 = cx + rx * rx0
py0 = cy + ry * ry0
px1 = cx + rx * rx1
py1 = cy + ry * ry1
px2 = cx + rx * rx2
py2 = cy + ry * ry2
px3 = cx + rx * rx3
py3 = cy + ry * ry3
# Debug points... comment this out!
# stroke(0)
# ellipse(px3, py3, 15, 15)
# ellipse(px0, py0, 5, 5)
# Drawing
if arc_type == 0: # 'normal' arc (not 'middle' nor 'naked')
beginShape()
if arc_type != 1: # if not 'middle'
vertex(px3, py3)
if theta < HALF_PI:
bezierVertex(px2, py2, px1, py1, px0, py0)
else:
# to avoid distortion, break into 2 smaller arcs
b_arc(cx, cy, w, h, startAngle, endAngle - theta / 2.0, arc_type=1)
b_arc(cx, cy, w, h, startAngle + theta / 2.0, endAngle, arc_type=1)
if arc_type == 0: # end of a 'normal' arc
endShape()

Wyświetl plik

@ -0,0 +1,86 @@
(lp0
(ipoly
Poly
p1
(dp2
S'closed'
p3
I01
sS'lw'
p4
I1
sS'id'
p5
I0
sS'pts'
p6
(lp7
(I0
I0
I-1
tp8
a(I6
I0
I1
tp9
a(I6
I6
I1
tp10
a(I0
I6
I0
tp11
asS'holes'
p12
(lp13
(lp14
asba(ipoly
Poly
p15
(dp16
S'closed'
p17
I01
sS'lw'
p18
I1
sS'id'
p19
I1
sS'pts'
p20
(lp21
(I-1
I-1
I0
tp22
a(I-6
I-1
I0
tp23
a(I-6
I-6
I0
tp24
a(I-1
I-6
I0
tp25
asS'holes'
p26
(lp27
(lp28
(I-2
I-3
I0
tp29
a(I-3
I-3
I1
tp30
a(I-2
I-2
I0
tp31
aasba.

Wyświetl plik

@ -0,0 +1,40 @@
"""
Alexandre B A Villares http://abav.lugaralgum.com - GPL v3
A helper for the Processing gifAnimation library https://github.com/extrapixel/gif-animation/tree/3.0
Download from https://github.com/villares/processing-play/blob/master/export_GIF/unzip_and_move_to_libraries_GifAnimation.zip
This helper was inspired by an example by Art Simon https://github.com/APCSPrinciples/AnimatedGIF/
# add at the start of your sketch:
add_library('gifAnimation')
from gif_exporter import gif_export
# add at the end of draw():
gif_export(GifMaker)
"""
def gif_export(GifMaker, # gets a reference to the library
filename="exported", # .gif will be added
repeat=0, # 0 makes it an "endless" animation
quality=255, # quality range 0 - 255
delay=200, # this is quick
frames=0, # 0 will stop on keyPressed or frameCount >= 100000
finish=False): # force stop
global gifExporter
try:
gifExporter
except NameError:
gifExporter = GifMaker(this, filename + ".gif")
gifExporter.setRepeat(repeat)
gifExporter.setQuality(quality)
gifExporter.setDelay(delay)
gifExporter.addFrame()
if frames == 0:
if keyPressed and key == "e":
finish = True
if finish:
gifExporter.finish()
print("gif saved")
exit()

Wyświetl plik

@ -0,0 +1,232 @@
from copy import deepcopy
from arcs import *
class Poly():
selected = -1
text_on = False
selected_drag = -1
drag_hole = -1
drag_pt = -1
id = 0
def __init__(self, pts, holes=None, closed=True, lw=1):
self.pts = pts
self.holes = holes if holes else [[]]
self.closed = closed
self.lw = lw
@classmethod
def setup_grid(cls, cell_size, order, x_offset, y_offset):
cls.cell_size = cell_size
cls.order = order
cls.x_offset, cls.y_offset = x_offset, y_offset
cls.polys = []
cls.text_on = False
def plot(self):
for i, p in enumerate(self.polys):
self.id = i if self == p else self.id
pushStyle()
strokeJoin(ROUND)
strokeWeight(self.lw)
if self.selected_drag == self.id:
stroke(200, 0, 0)
else:
stroke(0)
if len(self.pts) >= 2:
if self.closed:
fill(100)
else:
noFill()
beginShape()
Poly.draw_pts(self.pts)
for hole in self.holes:
beginContour()
Poly.draw_pts(hole)
endContour()
if self.closed:
endShape(CLOSE)
else:
endShape()
if self.text_on:
self.annotate_pts(self.id, self.pts, color(200, 0, 0), 5)
self.annotate_pts(self.id, self.holes[0], color(0, 0, 200), 5)
popStyle()
@classmethod
def draw_pts(cls, pts):
for i, pt in enumerate(pts):
x, y, corner = pt
sx, sy = cls.grid_to_screen(x, y)
if corner == 0:
vertex(sx, sy)
elif corner > 0:
pp = cls.grid_to_screen(pts[i - 1])
np = cls.grid_to_screen(pts[(i + 1) % len(pts)])
r = corner * cls.cell_size
b_roundedCorner((sx, sy), np, pp, r) # pt[2])
else:
if keyPressed:
vertex(sx, sy)
def remove_pt(self):
snap = self.mouse_snap()
if snap:
for pt in self.pts:
if pt[:2] == snap:
self.pts.remove(pt)
return True
for h in self.holes:
for pt in h:
if pt[:2] == snap:
h.remove(pt)
return True
def set_drag(self): # , io, jo):
snap = Poly.mouse_snap()
if snap:
for ipt, pt in enumerate(self.pts):
if pt[:2] == snap: # (io, jo):
Poly.drag_pt = ipt
return True
for ih, h in enumerate(self.holes):
for ipt, pt in enumerate(h):
if pt[:2] == snap: # (io, jo):
Poly.drag_hole = ih
Poly.drag_pt = ipt
return True
return False
@classmethod
def annotate_pts(cls, id, pts, c, scale_m=1):
strokeWeight(5)
textSize(12)
fill(c)
stroke(c)
for pt in pts:
i, j = pt[0], pt[1]
sx, sy = cls.grid_to_screen(i, j)
point(sx, sy)
text(str(id) + ":" + str((i * scale_m, j * scale_m)), sx, sy)
@classmethod
def draw_grid(cls):
stroke(128)
noFill()
for x in range(cls.order):
for y in range(cls.order):
rect(x * cls.cell_size, y * cls.cell_size,
cls.cell_size, cls.cell_size)
@staticmethod
def clockwise_sort(xy_pairs):
# https://stackoverflow.com/questions/51074984/sorting-according-to-clockwise-point-coordinates
data_len = len(xy_pairs)
if data_len > 2:
x, y = zip(*xy_pairs)
else:
return xy_pairs
centroid_x, centroid_y = sum(x) / data_len, sum(y) / data_len
xy_sorted = sorted(xy_pairs,
key=lambda p: atan2((p[1] - centroid_y), (p[0] - centroid_x)))
xy_sorted_xy = [
coord for pair in list(zip(*xy_sorted)) for coord in pair]
half_len = int(len(xy_sorted_xy) / 2)
return list(zip(xy_sorted_xy[:half_len], xy_sorted_xy[half_len:]))
@classmethod
def mouse_snap(cls):
for i in range(cls.order):
x = i * cls.cell_size
for j in range(cls.order):
y = j * cls.cell_size
# grid origin correction
io, jo = i - cls.x_offset, j - cls.y_offset
if dist(mouseX, mouseY, x, y) < cls.cell_size / 2:
return (io, jo)
return None
@classmethod
def mouse_pressed(cls):
if keyPressed and keyCode == CONTROL:
for p in cls.polys:
if p.remove_pt(): # io, jo):
return
else:
for ip, p in enumerate(cls.polys):
if p.set_drag(): # io, jo):
cls.selected_drag = ip
return
cls.selected_drag = -1 # click outside known vertices deselects
@classmethod
def mouse_dragged(cls):
if cls.selected_drag >= 0 and not keyPressed:
# a Poly point has been selected to be dragged
# and no modifier key is pressed...
if cls.drag_hole == -1: # if no hole was selected
poly = cls.polys[cls.selected_drag]
i, j = cls.screen_to_grid(mouseX, mouseY)
poly.pts[cls.drag_pt] = (i, j, poly.pts[cls.drag_pt][2])
else:
poly = cls.polys[cls.selected_drag]
hole = poly.holes[cls.drag_hole]
i, j = cls.screen_to_grid(mouseX, mouseY)
hole[cls.drag_pt] = (i, j, hole[cls.drag_pt][2])
elif cls.selected_drag >= 0 and key == "m":
poly = cls.polys[cls.selected_drag]
dragged_pt = poly.pts[cls.drag_pt]
mx, my = cls.screen_to_grid(mouseX, mouseY)
dx, dy = mx - dragged_pt[0], my - dragged_pt[1]
pts = poly.pts
for i, pt in enumerate(pts):
pts[i] = (pt[0] + dx, pt[1] + dy, pt[2] )
for hole in poly.holes:
for i, pt in enumerate(hole):
hole[i] = (pt[0] + dx, pt[1] + dy, pt[2])
@classmethod
def grid_to_screen(cls, *args):
if len(args) == 1:
x, y = args[0][0], args[0][1]
else:
x, y = args
return ((x + cls.x_offset) * cls.cell_size,
(y + cls.y_offset) * cls.cell_size)
@classmethod
def screen_to_grid(cls, x, y):
return (int(x / cls.cell_size) - cls.x_offset,
int(y / cls.cell_size) - cls.y_offset)
@classmethod
def mouse_released(cls):
if cls.selected_drag >= 0 and keyPressed and keyCode == SHIFT:
# a Poly point has been selected to be dragged
# and SHIFT key is pressed...
if cls.drag_hole == -1: # if no hole wase selected
poly = cls.polys[cls.selected_drag]
i, j = cls.screen_to_grid(mouseX, mouseY )
poly.pts.insert(cls.drag_pt, (i, j, 0))
else:
poly = cls.polys[cls.selected_drag]
hole = poly.holes[Poly.drag_hole]
i, j = cls.screen_to_grid(mouseX, mouseY )
hole.insert(cls.drag_pt, (i, j, 0))
# Poly.selected_drag = -1 # No poly selected
Poly.drag_hole = -1 # No hole selected
Poly.drag_pt = -1 # No point selected
@classmethod
def duplicate_selected(cls, off=1):
if Poly.selected_drag >= 0:
new_poly = deepcopy(cls.polys[cls.selected_drag])
for i, pt in enumerate(new_poly.pts):
new_poly.pts[i] = (pt[0] + off, pt[1] + off, pt[2])
for h in new_poly.holes:
for i, pt in enumerate(h):
h[i] = (pt[0] + off, pt[1] + off, pt[2])
cls.polys.append(new_poly)

Plik binarny nie jest wyświetlany.

Po

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

Wyświetl plik

@ -0,0 +1,86 @@
# Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day
"""
A minimal poly editor
- Add points
"""
import pickle
from poly import Poly
# add_library('GifAnimation')
# from gif_exporter import gif_export
def setup():
size(500, 500, P2D)
f = createFont("Fira Mono", 16)
textFont(f)
CELL_SIZE = 15
order = width // CELL_SIZE
x_offset = y_offset = int(order // 2)
Poly.setup_grid(CELL_SIZE, order, x_offset, y_offset)
p1 = Poly([(0, 0, -1), (6, 0, 1), (6, 6, 1), (0, 6, 0)])
Poly.polys.append(p1)
p2 = Poly([(-1, -1, 0), (-6, -1, 0), (-6, -6, 0), (-1, -6, 0)],
holes=[[(-2, -3, 0), (-3, -3, 1), (-2, -2, 0)]])
Poly.polys.append(p2)
def draw():
background(230)
# grade
Poly.draw_grid()
# polígonos
for p in Poly.polys:
p.plot()
def mousePressed():
Poly.mouse_pressed()
def mouseDragged():
Poly.mouse_dragged()
def mouseReleased():
Poly.mouse_released()
def keyPressed():
if key == "=":
Poly.selected_drag += 1
if Poly.selected_drag >= len(Poly.polys):
Poly.selected_drag = -1
if key == "d":
Poly.duplicate_selected()
if key == " " and Poly.selected_drag >= 0:
p = Poly.polys[Poly.selected_drag]
p.pts[:] = Poly.clockwise_sort(p.pts)
for h in p.holes:
h[:] = Poly.clockwise_sort(h)[::-1]
# if key == "g":
# gif_export(GifMaker, filename=SKETCH_NAME)
if key == "p":
saveFrame(SKETCH_NAME + ".png")
if key == "t":
Poly.text_on = not Poly.text_on
if key == "s":
with open("data/project.data", "wb") as file_out:
pickle.dump(Poly.polys, file_out)
println("project saved")
if key == "r":
with open("data/project.data", "rb") as file_in:
Poly.polys = pickle.load(file_in)
println("project loaded")
def settings():
from os import path
global SKETCH_NAME
SKETCH_NAME = path.basename(sketchPath())
OUTPUT = ".png"
println(
"""
![{0}](2019/{0}/{0}{1})
[{0}](https://github.com/villares/sketch-a-day/tree/master/2019/{0}) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)]
""".format(SKETCH_NAME, OUTPUT)
)