Build DXF-based R2C 3D viewer and local web server

This commit is contained in:
eglee committed 2026-10-20 21:23:57 +09:00
1 parent 277c7005ab
commit 11fd3badf6
8 files changed
+74912 -103

No files matched your search

+119
View File
@@ -0,0 +1,119 @@
import json
import io
import http.server
from urllib.parse import quote
import math
from collections import Counter
from pathlib import Path
import tempfile
import unittest
import r2c
class DrawingReconstructionTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.entities, cls.blocks, cls.raw = r2c.read_dxf(r2c.DEFAULT_DXF)
cls.model = r2c.extract_model(cls.entities, cls.raw)
cls.meshes = r2c.make_mesh(cls.model)
def test_diameter_and_dimensions_match_both_section_and_table(self):
m = self.model
self.assertEqual(m['diameter'], 1000)
self.assertAlmostEqual(m['length'], 1620)
self.assertAlmostEqual(m['width'], 1540)
self.assertAlmostEqual(m['height'], 1486)
self.assertAlmostEqual(m['axisHeight'], 666)
self.assertEqual(len(m['table']), 7)
self.assertEqual([r['D'] for r in m['table']], [700, 800, 900, 1000, 1100, 1200, 1350])
def test_inlet_is_rectangular_and_outlet_is_circular(self):
profiles = self.model['profiles']
self.assertEqual(profiles[0]['kind'], 'rectangle')
self.assertAlmostEqual(profiles[0]['halfWidth'] * 2, 1310)
self.assertAlmostEqual(profiles[0]['top'] + profiles[0]['bottom'], 1205)
self.assertEqual(profiles[2]['kind'], 'circle')
self.assertEqual(profiles[2]['radius'], 500)
self.assertEqual(profiles[2]['x'], 1400)
self.assertAlmostEqual(profiles[-1]['x'], 1620)
self.assertAlmostEqual(profiles[-1]['radius'], 615.8)
def test_pipe_is_actual_revolved_polyline(self):
self.assertEqual(self.model['evidence']['pipeProfile'], '195')
profile = self.model['pipeProfile']
self.assertAlmostEqual(max(p[0] for p in profile), 2620)
self.assertAlmostEqual(min(p[1] for p in profile), 500)
self.assertAlmostEqual(max(p[1] for p in profile), 666)
def test_closed_meshes_have_no_boundary_edges(self):
for name, mesh in self.meshes.items():
edges = Counter()
for face in mesh['faces']:
self.assertEqual(len(set(face)), len(face))
for a, b in zip(face, face[1:] + face[:1]):
edges[tuple(sorted((a, b)))] += 1
self.assertTrue(all(count == 2 for count in edges.values()), name)
self.assertTrue(all(math.isfinite(n) for p in mesh['vertices'] for n in p))
def test_r2c_bounds_match_native_drawing(self):
pts = self.meshes['r2c']['vertices']
for axis, expected in enumerate([1620, 1540, 1486]):
self.assertAlmostEqual(max(p[axis] for p in pts) - min(p[axis] for p in pts), expected, places=5)
def test_svg_preserves_curves_sections_and_korean_text(self):
svg, omitted = r2c.drawing_svg(self.entities, self.blocks)
self.assertIn('단면 A-A', svg)
self.assertIn('단면 B-B', svg)
self.assertIn('1,620', svg)
self.assertNotIn('%%C', svg)
self.assertGreater(svg.count('<polyline'), 800)
self.assertIn('HATCH', omitted)
def test_static_package_has_visible_geometry_without_javascript(self):
with tempfile.TemporaryDirectory() as directory:
m = r2c.build(output=directory, update_entry=False)
out = Path(directory)
source = (out / 'index.html').read_text()
self.assertNotIn('__INITIAL_SCENE__', source)
self.assertNotIn('__MODEL_JSON__', source)
self.assertNotIn('__DRAWING_SVG__', source)
before_script = source.split('<script')[0]
self.assertGreater(before_script.count('<polygon'), 900)
self.assertTrue((out / 'drawing' / r2c.DEFAULT_DXF.name).is_file())
self.assertEqual(json.loads((out / 'model.json').read_text())['sha256'], m['sha256'])
self.assertTrue((out / 'model.obj').read_text().startswith('# R2C'))
def test_web_handler_serves_generated_files_only(self):
class Handler(http.server.SimpleHTTPRequestHandler):
def log_message(self, *args):
pass
with tempfile.TemporaryDirectory() as directory:
r2c.build(output=directory, update_entry=False)
for path, status, content in [
('/index.html', b'200 OK', b'<polygon'),
('/model.obj', b'200 OK', b'# R2C'),
('/drawing/' + quote(r2c.DEFAULT_DXF.name), b'200 OK', b'SECTION'),
('/.git/config', b'404', b'File not found'),
]:
handler = object.__new__(Handler)
handler.directory = directory
handler.path = path
handler.command = 'GET'
handler.request_version = 'HTTP/1.1'
handler.requestline = 'GET ' + path + ' HTTP/1.1'
handler.headers = {}
handler.wfile = io.BytesIO()
handler.do_GET()
response = handler.wfile.getvalue()
self.assertIn(status, response.split(b'\r\n')[0])
self.assertIn(content, response)
def test_unrelated_drawing_does_not_produce_a_guessed_model(self):
with self.assertRaisesRegex(ValueError, '변화치수표'):
r2c.extract_model([], b'')
if __name__ == '__main__':
unittest.main()