User:DressyPear4/RetanguloArredondado
Jump to navigation
Jump to search

Imagem.gif, clique para visualizar.
Criar um retângulo arredondado
Para geometrias que precisam de suavização das bordas e controle do tamanho
Como funciona?
- Selecione uma geometria com quatro lados
- Abrirá um diálogo com controles
- Ajuste de largura
- Ajuste de altura
- Ajuste de circunferência
- Adição/subtração de nós
Demonstração

Código
from org.openstreetmap.josm.gui import MainApplication, Notification
from org.openstreetmap.josm.data.osm import Node, Way
from org.openstreetmap.josm.data.projection import ProjectionRegistry
from org.openstreetmap.josm.data.coor import EastNorth, LatLon
from org.openstreetmap.josm.data import UndoRedoHandler
from org.openstreetmap.josm.command import SequenceCommand, AddCommand
from javax.swing import JPanel, JLabel, JButton, JOptionPane, BoxLayout, Box, UIManager
from java.awt import Dimension, FlowLayout
import math
WIDTH = [0.0]
HEIGHT = [0.0]
RADIUS = [0.0]
SEGMENTS_PER_CORNER = [4]
preview_cmd = [None]
center_poly = [None]
angle_poly = [0.0]
def create_rounded_corner(center, start_angle, end_angle, radius, segments):
points = []
for i in range(segments + 1):
angle = math.radians(start_angle + (end_angle - start_angle) * i / segments)
x = center.getX() + radius * math.cos(angle)
y = center.getY() + radius * math.sin(angle)
points.append(EastNorth(x, y))
return points
def rotate_point(p, center, angle_rad):
dx = p.getX() - center.getX()
dy = p.getY() - center.getY()
cos_a = math.cos(angle_rad)
sin_a = math.sin(angle_rad)
x_new = center.getX() + dx * cos_a - dy * sin_a
y_new = center.getY() + dx * sin_a + dy * cos_a
return EastNorth(x_new, y_new)
def is_clockwise(coords):
s = 0.0
for i in range(len(coords) - 1):
p1 = coords[i]
p2 = coords[i + 1]
s += (p2.getX() - p1.getX()) * (p2.getY() + p1.getY())
return s > 0
def desenhar_preview():
if preview_cmd[0]:
UndoRedoHandler.getInstance().undo()
preview_cmd[0] = None
projection = ProjectionRegistry.getProjection()
dataset = MainApplication.getLayerManager().getEditDataSet()
if not dataset or center_poly[0] is None:
return
height = max(WIDTH[0], 0.1)
width = max(HEIGHT[0], 0.1)
radius = min(RADIUS[0], width / 2, height / 2)
segs = SEGMENTS_PER_CORNER[0]
cx, cy = center_poly[0].getX(), center_poly[0].getY()
x0, y0 = cx - width / 2, cy - height / 2
x1, y1 = cx + width / 2, cy + height / 2
en_tl = EastNorth(x0 + radius, y1 - radius)
en_tr = EastNorth(x1 - radius, y1 - radius)
en_br = EastNorth(x1 - radius, y0 + radius)
en_bl = EastNorth(x0 + radius, y0 + radius)
path = []
path += create_rounded_corner(en_tl, 180, 90, radius, segs)
path += create_rounded_corner(en_tr, 90, 0, radius, segs)
path += create_rounded_corner(en_br, 360, 270, radius, segs)
path += create_rounded_corner(en_bl, 270, 180, radius, segs)
path_rotated = [rotate_point(p, center_poly[0], angle_poly[0]) for p in path]
nodes = []
commands = []
for en in path_rotated:
latlon = projection.eastNorth2latlon(en)
node = Node(latlon)
node.setModified(True)
commands.append(AddCommand(dataset, node))
nodes.append(node)
nodes.append(nodes[0])
coords = [projection.latlon2eastNorth(n.getCoor()) for n in nodes]
if is_clockwise(coords):
nodes.reverse()
way = Way()
way.setNodes(nodes)
way.setModified(True)
commands.append(AddCommand(dataset, way))
cmd = SequenceCommand("Preview retângulo arredondado", commands)
UndoRedoHandler.getInstance().add(cmd)
preview_cmd[0] = cmd
def main():
dataset = MainApplication.getLayerManager().getEditDataSet()
if not dataset:
Notification("Nenhuma camada ativa")\
.setIcon(UIManager.getIcon("OptionPane.errorIcon"))\
.show()
return
selection = [p for p in dataset.getSelected() if isinstance(p, Way) and p.isArea()]
if len(selection) != 1:
Notification(u"Selecione um polígono fechado como referência")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()
return
poly = selection[0]
proj = ProjectionRegistry.getProjection()
coords = [proj.latlon2eastNorth(n.getCoor()) for n in poly.getNodes()]
xs = [c.getX() for c in coords]
ys = [c.getY() for c in coords]
min_x, max_x = min(xs), max(xs)
min_y, max_y = min(ys), max(ys)
center_poly[0] = EastNorth((min_x + max_x) / 2, (min_y + max_y) / 2)
dx = coords[1].getX() - coords[0].getX()
dy = coords[1].getY() - coords[0].getY()
angle_poly[0] = math.atan2(dy, dx)
WIDTH[0] = max_x - min_x
HEIGHT[0] = max_y - min_y
RADIUS[0] = min(WIDTH[0], HEIGHT[0]) / 4.0
SEGMENTS_PER_CORNER[0] = 4
WIDTH_DEFAULT = WIDTH[0]
HEIGHT_DEFAULT = HEIGHT[0]
RADIUS_DEFAULT = RADIUS[0]
SEGMENTS_DEFAULT = SEGMENTS_PER_CORNER[0]
def update_label():
label_largura.setText("Largura: {:.1f}".format(WIDTH[0]))
label_altura.setText("Altura: {:.1f}".format(HEIGHT[0]))
label_raio.setText("Raio: {:.1f}".format(RADIUS[0]))
label_segmentos.setText("Segmentos: {}".format(SEGMENTS_PER_CORNER[0]))
def make_button(text, callback):
btn = JButton(text)
btn.setPreferredSize(Dimension(40, 30))
btn.addActionListener(callback)
return btn
def change_value(param, delta, min_val=1):
param[0] = max(min_val, param[0] + delta)
update_label()
desenhar_preview()
def change_float(param, delta, min_val=0.1):
param[0] = max(min_val, round(param[0] + delta, 1))
update_label()
desenhar_preview()
panel = JPanel()
panel.setLayout(BoxLayout(panel, BoxLayout.Y_AXIS))
# Largura
global label_largura
label_largura = JLabel()
row1 = JPanel()
row1.setLayout(BoxLayout(row1, BoxLayout.X_AXIS))
row1.add(label_largura)
row1.add(Box.createHorizontalGlue())
b1 = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0))
b1.add(make_button("-", lambda e: change_float(WIDTH, -1.0)))
b1.add(make_button("+", lambda e: change_float(WIDTH, 1.0)))
row1.add(b1)
panel.add(row1)
# Altura
global label_altura
label_altura = JLabel()
row2 = JPanel()
row2.setLayout(BoxLayout(row2, BoxLayout.X_AXIS))
row2.add(label_altura)
row2.add(Box.createHorizontalGlue())
b2 = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0))
b2.add(make_button("-", lambda e: change_float(HEIGHT, -1.0)))
b2.add(make_button("+", lambda e: change_float(HEIGHT, 1.0)))
row2.add(b2)
panel.add(row2)
# Raio
global label_raio
label_raio = JLabel()
row3 = JPanel()
row3.setLayout(BoxLayout(row3, BoxLayout.X_AXIS))
row3.add(label_raio)
row3.add(Box.createHorizontalGlue())
b3 = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0))
b3.add(make_button("-", lambda e: change_float(RADIUS, -0.5)))
b3.add(make_button("+", lambda e: change_float(RADIUS, 0.5)))
row3.add(b3)
panel.add(row3)
# Segmentos
global label_segmentos
label_segmentos = JLabel()
row4 = JPanel()
row4.setLayout(BoxLayout(row4, BoxLayout.X_AXIS))
row4.add(label_segmentos)
row4.add(Box.createHorizontalGlue())
b4 = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0))
b4.add(make_button("-", lambda e: change_value(SEGMENTS_PER_CORNER, -1)))
b4.add(make_button("+", lambda e: change_value(SEGMENTS_PER_CORNER, 1)))
row4.add(b4)
panel.add(row4)
btn_reset = JButton("Resetar")
btn_reset.setAlignmentX(0.5)
def reset_vals(e):
WIDTH[0] = WIDTH_DEFAULT
HEIGHT[0] = HEIGHT_DEFAULT
RADIUS[0] = RADIUS_DEFAULT
SEGMENTS_PER_CORNER[0] = SEGMENTS_DEFAULT
update_label()
desenhar_preview()
btn_reset.addActionListener(reset_vals)
panel.add(Box.createRigidArea(Dimension(0, 10)))
panel.add(btn_reset)
update_label()
desenhar_preview()
result = JOptionPane.showConfirmDialog(None, panel, u"Desenhar retângulo arredondado", JOptionPane.OK_CANCEL_OPTION)
if preview_cmd[0]:
UndoRedoHandler.getInstance().undo()
preview_cmd[0] = None
if result == JOptionPane.OK_OPTION:
desenhar_preview()
Notification(u"Retângulo criado com sucesso")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()
main()