Build DXF-based R2C 3D viewer and local web server
This commit is contained in:
1 parent
277c7005ab
commit
11fd3badf6
8 files changed
+74912
-103
No files matched your search
@@ -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()
|
||||
Reference in new issue
Block a user