YAML Metadata Warning: empty or missing yaml metadata in repo card
Check out the documentation for more information.
import ezdxf
import matplotlib.pyplot as plt
from matplotlib.patches import Arc
import numpy as np
import math
# ============== ์ฌ์ฉ์ ์
๋ ฅ ==============
dxf_path = "plan.dxf"
# ํ์ด๋ผ์ดํธํ ๋
ธ๋ ๊ทธ๋ฃน (์ํ๋ ๋งํผ ์ถ๊ฐ)
# points: ์ฌ์ฉ์๊ฐ ์ง์ ํ ์ขํ(๋๋ต์น์ฌ๋ ์ค๋
๋จ), label์ ์ต์
selected_groups = {
"CHECK": {
"color": "red",
"points": [(100.0, 200.0), (150.2, 80.7)], # ์์ ์ขํ
"labels": ["N1", "N2"], # ์์ผ๋ฉด ์๋ ๋ฒํธ
},
"MONITOR": {
"color": "orange",
"points": [(300.0, 210.0)],
"labels": None,
},
}
snap_tol = 1.0 # ์ค๋
ํ์ฉ์ค์ฐจ(๋๋ฉด ๋จ์). ๋๋ฉด ์ค์ผ์ผ์ ๋ง์ถฐ ์กฐ์
marker_size = 8 # ๋ง์ปค ํฝ์
ํฌ๊ธฐ
label_fontsize = 6 # ๋ผ๋ฒจ ํฐํธ ํฌ๊ธฐ
# ============== DXF ๋ก๋ ==============
doc = ezdxf.readfile(dxf_path)
msp = doc.modelspace()
fig, ax = plt.subplots(figsize=(10, 10))
# ---------------------------
# ๊ณตํต ์ ํธ
# ---------------------------
def plot_segments(ax, points, closed=False, lw=0.6, color="black"):
if len(points) < 2:
return
xs, ys = zip(*points)
ax.plot(xs, ys, linewidth=lw, color=color)
if closed and (points[0] != points[-1]):
ax.plot([points[-1][0], points[0][0]],
[points[-1][1], points[0][1]], linewidth=lw, color=color)
def plot_lwpolyline(ax, e):
"""LWPOLYLINE์ bulge(ํธ)๋ฅผ ๊ทผ์ฌํ์ฌ ๊ทธ๋ฆผ."""
pts = list(e.get_points("xyb"))
if not pts:
return
approx = []
for i in range(len(pts) - 1 + int(e.closed)):
x1, y1, b1 = pts[i % len(pts)]
x2, y2, _ = pts[(i+1) % len(pts)]
p1 = np.array([x1, y1])
p2 = np.array([x2, y2])
if abs(b1) < 1e-12:
approx += [tuple(p1), tuple(p2)]
else:
delta = 4 * math.atan(b1) # ์ค์ฌ๊ฐ
chord = p2 - p1
L = np.linalg.norm(chord)
if L < 1e-12:
continue
R = (L/2) / abs(math.sin(delta/2))
mid = (p1 + p2) / 2
n = np.array([-(chord[1]), chord[0]]) / (L + 1e-12)
h = R * math.cos(delta/2)
center = mid + np.sign(b1) * h * n
a1 = math.atan2(p1[1]-center[1], p1[0]-center[0])
a2 = math.atan2(p2[1]-center[1], p2[0]-center[0])
def angle_range(a_start, a_end, ccw=True, steps=32):
if ccw:
if a_end <= a_start:
a_end += 2*math.pi
return np.linspace(a_start, a_end, steps)
else:
if a_end >= a_start:
a_end -= 2*math.pi
return np.linspace(a_start, a_end, steps)
ccw = (b1 > 0)
angles = angle_range(a1, a2, ccw=ccw, steps=max(16, int(abs(delta)*16)))
for t in angles:
approx.append((center[0] + R*math.cos(t), center[1] + R*math.sin(t)))
cleaned = []
for p in approx:
if not cleaned or (abs(cleaned[-1][0]-p[0])>1e-9 or abs(cleaned[-1][1]-p[1])>1e-9):
cleaned.append(p)
plot_segments(ax, cleaned, closed=e.closed, lw=0.6)
def draw_basic_entity(ax, ent):
"""INSERT ์ ๊ฐ๋ ๊ฐ์ ์ํฐํฐ ํฌํจ, ๊ฐ๋ณ ์ํฐํฐ๋ฅผ ๊ทธ๋ฆผ."""
t = ent.dxftype()
if t == "LINE":
x = [ent.dxf.start.x, ent.dxf.end.x]
y = [ent.dxf.start.y, ent.dxf.end.y]
ax.plot(x, y, linewidth=0.6, color="black")
elif t == "LWPOLYLINE":
plot_lwpolyline(ax, ent)
elif t == "POLYLINE":
pts = [(v.dxf.location.x, v.dxf.location.y) for v in ent.vertices]
plot_segments(ax, pts, closed=getattr(ent, "is_closed", False), lw=0.6)
elif t == "ARC":
c = ent.dxf.center; r = ent.dxf.radius
arc = Arc((c.x, c.y), width=2*r, height=2*r, angle=0,
theta1=ent.dxf.start_angle, theta2=ent.dxf.end_angle,
linewidth=0.6, color="black")
ax.add_patch(arc)
elif t == "CIRCLE":
c = ent.dxf.center; r = ent.dxf.radius
ax.add_patch(plt.Circle((c.x, c.y), r, fill=False, linewidth=0.6, color="black"))
elif t == "ELLIPSE":
center = np.array([ent.dxf.center.x, ent.dxf.center.y])
major = np.array([ent.dxf.major_axis.x, ent.dxf.major_axis.y])
ratio = ent.dxf.ratio
t0 = ent.dxf.start_param; t1 = ent.dxf.end_param
u = major
v = np.array([-major[1], major[0]])
v = v / (np.linalg.norm(v) + 1e-12) * (np.linalg.norm(major) * ratio)
ts = np.linspace(t0, t1, 200)
xs = center[0] + u[0]*np.cos(ts) + v[0]*np.sin(ts)
ys = center[1] + u[1]*np.cos(ts) + v[1]*np.sin(ts)
ax.plot(xs, ys, linewidth=0.6, color="black")
elif t == "SPLINE":
pts = ent.approximate(segments=200)
xs, ys = zip(*[(p[0], p[1]) for p in pts])
ax.plot(xs, ys, linewidth=0.6, color="black")
elif t == "TEXT":
ins = ent.dxf.insert; text = ent.dxf.text
height = ent.dxf.height if ent.dxf.height else 2.5
rot = ent.dxf.rotation if ent.dxf.hasattr("rotation") else 0.0
ax.text(ins.x, ins.y, text, fontsize=height, rotation=rot,
rotation_mode="anchor", ha="left", va="baseline", color="black")
elif t in ("MTEXT", "ATTRIB"):
ins = ent.dxf.insert
text = ent.plain_text() if t == "MTEXT" else ent.dxf.text
rot = ent.dxf.rotation if ent.dxf.hasattr("rotation") else 0.0
h = getattr(ent.dxf, "char_height", None) or getattr(ent.dxf, "height", None) or 2.5
ax.text(ins.x, ins.y, text, fontsize=h, rotation=rot,
rotation_mode="anchor", ha="left", va="top", color="black")
elif t == "HATCH":
for path in ent.paths:
if path.PATH_TYPE_EDGE:
pts = []
for edge in path.edges:
typ = edge.EDGE_TYPE
if typ == "LineEdge":
pts += [(edge.start[0], edge.start[1]), (edge.end[0], edge.end[1])]
elif typ == "ArcEdge":
cx, cy = edge.center; r = edge.radius
a0 = math.radians(edge.start_angle); a1 = math.radians(edge.end_angle)
ts = np.linspace(a0, a1, 50)
pts += [(cx + r*np.cos(t), cy + r*np.sin(t)) for t in ts]
elif typ == "EllipseEdge":
(cx, cy) = edge.center
major = np.array(edge.major_axis); ratio = edge.ratio
t0, t1 = edge.start_param, edge.end_param
u = major; v = np.array([-major[1], major[0]])
v = v / (np.linalg.norm(v)+1e-12) * (np.linalg.norm(major)*ratio)
ts = np.linspace(t0, t1, 100)
pts += [(cx + u[0]*np.cos(t) + v[0]*np.sin(t),
cy + u[1]*np.cos(t) + v[1]*np.sin(t)) for t in ts]
elif typ == "SplineEdge":
ap = edge.spline.approximate(segments=100)
pts += [(p[0], p[1]) for p in ap]
if len(pts) >= 2:
plot_segments(ax, pts, lw=0.4)
elif path.PATH_TYPE_POLYLINE:
pts = [(v[0], v[1]) for v in path.vertices]
plot_segments(ax, pts, lw=0.4)
# ---------------------------
# 1) ๊ธฐ๋ณธ ์ํฐํฐ ๊ทธ๋ฆฌ๊ธฐ(INSERT ์ ์ธ)
# ---------------------------
for e in msp:
if e.dxftype() == "INSERT":
continue
draw_basic_entity(ax, e)
# ---------------------------
# 2) ๋ธ๋ก(INSERT) ์ ๊ฐ + DIMENSION
# ---------------------------
for br in msp.query("INSERT"):
try:
for ve in br.virtual_entities():
draw_basic_entity(ax, ve)
except Exception:
continue
for dim in msp.query("DIMENSION"):
try:
dim.render()
for ve in dim.virtual_entities():
draw_basic_entity(ax, ve)
except Exception:
continue
# ---------------------------
# 3) ๋
ธ๋ ํ๋ณด ์ถ์ถ (๋์ /๋ฒํ
์ค ์ค์ฌ)
# ---------------------------
node_candidates = []
def add_node(p):
node_candidates.append((float(p[0]), float(p[1])))
# LINE ๋์
for e in msp.query("LINE"):
add_node((e.dxf.start.x, e.dxf.start.y))
add_node((e.dxf.end.x, e.dxf.end.y))
# LWPOLYLINE: ๋ฒํ
์ค ์ขํ(ํธ ์ค๊ฐ์ ๊น์ง ๋ค ๋ฃ์ผ๋ฉด ๋๋ฌด ๋ง์์ ธ์ ๋ฒํ
์ค๋ง)
for e in msp.query("LWPOLYLINE"):
for (x, y, *_) in e.get_points("xyb"):
add_node((x, y))
# POLYLINE: ๋ฒํ
์ค
for e in msp.query("POLYLINE"):
for v in e.vertices:
add_node((v.dxf.location.x, v.dxf.location.y))
# ARC/CIRCLE ์ค์ฌ์ ๋
ธ๋๋ก ์ฐ๊ณ ์ถ๋ค๋ฉด ์๋ ์ฃผ์ ํด์
# for e in msp.query("ARC"):
# add_node((e.dxf.center.x, e.dxf.center.y))
# for e in msp.query("CIRCLE"):
# add_node((e.dxf.center.x, e.dxf.center.y))
# ์ค๋ณต ์ ๊ฑฐ(๊ฒฉ์ ์ค๋
)
def snap_key(p, tol=1e-6):
return (round(p[0]/tol), round(p[1]/tol))
uniq = {}
for p in node_candidates:
k = snap_key(p, 1e-6)
if k not in uniq:
uniq[k] = p
node_candidates = list(uniq.values())
# ---------------------------
# 4) ์ ํ ๋
ธ๋ ์ค๋
& ํ์ด๋ผ์ดํธ
# ---------------------------
def find_nearest_node(pt, candidates, tol):
"""pt์ ๊ฐ์ฅ ๊ฐ๊น์ด ํ๋ณด๋ฅผ ์ฐพ๊ณ , ๊ฑฐ๋ฆฌ๊ฐ tol๋ณด๋ค ํฌ๋ฉด None."""
px, py = pt
best = None; best_d2 = None
for cx, cy in candidates:
d2 = (cx - px)**2 + (cy - py)**2
if (best_d2 is None) or (d2 < best_d2):
best_d2 = d2; best = (cx, cy)
if best is None:
return None
dist = math.sqrt(best_d2)
return best if dist <= tol else None
def draw_marker(ax, x, y, color="red", size=8, zorder=10):
# ํ๋ฉด ํฝ์
๊ณ ์ ํฌ๊ธฐ ๋ง์ปค(๋๋ฉด ์ถ์ฒ ๋ฌด๊ด)
ax.scatter([x], [y], s=size**2, c=color, marker='o', zorder=zorder, linewidths=0.5, edgecolors="black")
# ๊ทธ๋ฃน๋ณ๋ก ์ค๋
/ํ์
legend_handles = []
for gname, cfg in selected_groups.items():
color = cfg.get("color", "red")
pts = cfg.get("points", [])
labels= cfg.get("labels", None)
placed = []
for i, pt in enumerate(pts):
snapped = find_nearest_node(pt, node_candidates, snap_tol)
if snapped is None:
# ๊ฐ๊น์ด ๋
ธ๋๊ฐ ์์ผ๋ฉด ์๋ ๋๋ต ์ขํ์ ๋ง์ปค(์ ๋ค๋ฅด๊ฒ ํ์)
draw_marker(ax, pt[0], pt[1], color="gray", size=marker_size, zorder=12)
if labels:
ax.text(pt[0], pt[1], labels[i], fontsize=label_fontsize, color="gray",
ha="left", va="bottom", zorder=12)
continue
x, y = snapped
draw_marker(ax, x, y, color=color, size=marker_size, zorder=13)
if labels:
ax.text(x, y, labels[i], fontsize=label_fontsize, color=color,
ha="left", va="bottom", zorder=13)
placed.append((x, y))
# ๋ฒ๋ก์ฉ ๋๋ฏธ(์ ํ์ฌํญ)
lh = ax.scatter([], [], s=marker_size**2, c=color, marker='o', edgecolors="black", label=gname)
legend_handles.append(lh)
if legend_handles:
ax.legend(loc="upper right", fontsize=8, frameon=True)
# ---------------------------
# 5) ๋ณด๊ธฐ/์ ์ฅ
# ---------------------------
ax.set_aspect("equal")
ax.axis("off")
# ๋๋ฉด ์ ์ฒด extents ์๋ ํฌ๋กญ(์ฌ๋ฐฑ ํฌํจ)
try:
# ezdxf์ extents ๊ณ์ฐ์ ์ฐ๊ณ ์ถ๋ค๋ฉด:
# from ezdxf.addons.drawing import layout
# ext = layout.Layout(msp).bbox() # ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์์
# ์ฌ๊ธฐ์๋ ์ฐํฌ๋ก ์ถ์ :
xs, ys = [], []
for (x, y) in node_candidates:
xs.append(x); ys.append(y)
if xs and ys:
margin = 0.05 # 5% ์ฌ๋ฐฑ
xmin, xmax = min(xs), max(xs)
ymin, ymax = min(ys), max(ys)
dx = xmax - xmin; dy = ymax - ymin
ax.set_xlim(xmin - dx*margin, xmax + dx*margin)
ax.set_ylim(ymin - dy*margin, ymax + dy*margin)
except Exception:
pass
plt.savefig("plan_with_nodes.png", dpi=300, bbox_inches="tight", pad_inches=0.02)
plt.savefig("plan_with_nodes.pdf", bbox_inches="tight", pad_inches=0.02)
plt.close()
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐ Ask for provider support