ทำชุดแผ่นแบบจากโมเดล SketchUp วางเลขกับเส้นระยะอัตโนมัติ
ขั้นที่คนมองข้ามที่สุดของงานเขียนแบบด้วยคอมพิวเตอร์ คือขั้นสุดท้าย ตอนที่ต้องย่อโมเดลสามมิติทั้งก้อนลงมาเป็นกระดาษ A4 ที่คนอ่านรู้เรื่อง มีเลขกำกับครบ เส้นบอกระยะไม่ทับกัน และบอกได้ว่ามาตราส่วนเท่าไร
พอดีผมทำโมเดลผังโรงงานเสร็จแล้ว ลูกค้าถามคำถามที่ตอบไม่ได้ทันทีว่า "แล้วเอาไปยื่นยังไง" เพราะสิ่งที่หน่วยงานรับคือชุดเอกสารกระดาษ ที่มีผังรวมของทั้งที่ดิน มีเลขกำกับอาคารทุกหลังพร้อมรายการว่าหลังไหนใช้ทำอะไร มีระยะกำกับ มีทิศเหนือ มีมาตราส่วน และมีหัวกระดาษกับช่องลงนาม
จะไล่ลากเส้นบอกระยะทีละเส้นในโปรแกรมก็ทำได้ แต่พอลูกค้าขอแก้ผังรอบที่สามผมก็ยอมแพ้ครับ เลยเปลี่ยนเป็นเขียนสคริปต์ให้มันสร้างชุดแผ่นแบบใหม่ทั้งชุดจากโมเดลปัจจุบันทุกครั้งที่สั่ง
ซีรีส์ที่ผมเขียนไว้ก่อนหน้าเป็นเรื่องการสร้างโมเดล — ตั้งแต่ เตรียมเครื่อง ต่อปลั๊กอิน MCP ไปจนถึง สั่งสร้างโครงเหล็กแล้ววิเคราะห์ FEA ส่วนบทความนี้คือปลายทางอีกด้าน คือออกแบบเสร็จแล้วทำแผ่นแบบส่งราชการ คนละงานกันคนละเครื่องมือกัน
ขั้นแรก ดึงรูปทรงออกจากโมเดลมาเป็นตัวเลข
ผมไม่ให้สคริปต์วาดจากโมเดลตรง ๆ แต่ให้ดึงข้อมูลออกมาเป็นตัวเลขก่อน แล้วค่อยเอาตัวเลขไปทำงานต่อในฝั่ง Python เหตุผลคือรันซ้ำเร็ว แก้ง่าย และไม่ต้องเปิด SketchUp ค้างไว้ตลอดเวลาที่นั่งจัดหน้ากระดาษ
โมเดลที่ลากด้วยมือมักจะเป็นเส้นล้วน ไม่มีหน้า เพราะคนวาดลากขอบอาคารแล้วจบ เลยต้องสร้างหน้าชั่วคราวขึ้นมาก่อนถึงจะถามพื้นที่กับกรอบได้
m = Sketchup.active_model
e = m.entities
q = 25.4 # inch -> mm
hide = /Terrain|Map |3D Bldgs|Footprints/ # เลเยอร์ภาพถ่ายดาวเทียม ไม่เอามาคิด
m.start_operation('tmp faces', true)
e.grep(Sketchup::Edge).each { |ed| ed.find_faces if ed.valid? }
e.grep(Sketchup::Group).each { |g|
next if g.layer.name =~ hide
g.entities.grep(Sketchup::Edge).each { |ed| ed.find_faces if ed.valid? }
}
out = []
e.grep(Sketchup::Face).each do |f|
next if f.layer.name =~ hide
b = f.bounds
a = f.area * 0.00064516 # ตร.นิ้ว -> ตร.ม.
next if a < 3 # ตัดเศษเส้นเล็ก ๆ ทิ้ง
out << [(b.center.x.to_f * q).round, (b.center.y.to_f * q).round, a.round,
((b.max.x.to_f - b.min.x.to_f) * q).round,
((b.max.y.to_f - b.min.y.to_f) * q).round]
end
m.abort_operation # ทิ้งหน้าชั่วคราวทั้งหมด โมเดลกลับเป็นเหมือนเดิม
"N=#{out.size} :: " + out.map { |a| a.join(',') }.join(' ; ')
สองอย่างที่ต้องระวัง หนึ่งคือ 0.00064516 ตัวคูณแปลงตารางนิ้วเป็นตารางเมตร SketchUp คิดทุกอย่างเป็นนิ้วข้างใน ไม่ว่าจะตั้งหน่วยหน้าจอเป็นอะไร สองคือ abort_operation ตอนท้าย ทำให้หน้าที่สร้างขึ้นมาชั่วคราวหายไปหมด ไม่ทิ้งขยะไว้ในไฟล์ลูกค้า
กรอบของหน้าที่อยู่ในกลุ่มเป็นพิกัด local ของกลุ่มนั้น ถ้ากลุ่มเคยถูกเลื่อนหรือหมุนมาก่อน ต้อง b.center.transform(g.transformation) ก่อนใช้ ไม่งั้นอาคารจะไปโผล่ผิดที่บนแผ่นแบบ ทั้งที่ในโมเดลอยู่ถูกทุกอย่าง
สี่เหลี่ยมที่เอียง: กรอบสี่เหลี่ยมมันโกหก
ผังที่ดินจริงไม่ค่อยตั้งฉากกับทิศเหนือ อาคารเลยเอียงตามแนวรั้วไปด้วย ปัญหาคือ bounds คืนกรอบที่ขนานแกนเสมอ อาคารกว้าง 20 ยาว 30 ที่เอียง 30 องศา จะได้กรอบราว 32 x 36 ซึ่งไม่ใช่ขนาดที่จะเขียนลงแบบ
แต่เรามีของสองอย่างคือกรอบ กับพื้นที่จริง พอจะแก้ย้อนกลับหาด้านจริงได้ ถ้ารู้ว่ามันเป็นสี่เหลี่ยมมุมฉากที่เอียงอยู่ในกรอบนั้น
import math
def side_lengths(w_mm, h_mm, area_m2):
"""คืนด้านกว้าง-ยาวจริงของสี่เหลี่ยมที่เอียง จากกรอบ bbox + พื้นที่"""
W, H, A = w_mm / 1000.0, h_mm / 1000.0, float(area_m2)
q = 4 * A
p = -(W * W + H * H)
r = W * H - A
disc = p * p - 4 * q * r
if A <= 0 or disc < 0 or q == 0:
return W, H # ไม่เอียง หรือแก้ไม่ได้ -> ใช้กรอบไปเลย
k = max(0.0, min(0.5, (-p - math.sqrt(disc)) / (2 * q)))
s2 = W * W + H * H - 4 * A * k
su = math.sqrt(max(s2 + 2 * A, 1.0))
d = math.sqrt(su * su - 4 * A) if su * su > 4 * A else 0.0
b1, b2 = (su + d) / 2.0, (su - d) / 2.0
return max(b1, b2), min(b1, b2)
เขียนตัวเลขผิดลงแบบ
อาคารที่เอียงจะได้ขนาดใหญ่เกินจริงทุกหลัง และยิ่งเอียงใกล้ 45 องศายิ่งเพี้ยนมาก คนที่เอาแบบไปใช้ต่อจะกะพื้นที่ผิดตั้งแต่ต้น
ได้ด้านจริงทั้งสองด้าน
ใช้กรอบกับพื้นที่ที่โมเดลบอกมา แก้สมการหาด้านจริง แล้วปัดให้ลงท้าย .0 หรือ .5 ตามธรรมเนียมที่ช่างอ่านแล้วใช้ได้จริง
ตอนแสดงผมปัดระยะให้ลงท้ายศูนย์หรือห้าเสมอ เพราะแบบระดับผังรวมไม่มีใครไปวัดกันที่เซนติเมตร และตัวเลขทศนิยมสองตำแหน่งบนผัง 1:1250 อ่านไม่ออกอยู่ดี
def r5(v):
"""ปัดระยะให้ลงท้าย .0 หรือ .5"""
return round(v * 2.0) / 2.0
เรนเดอร์ผังเป็นภาพเส้นดำบนพื้นขาว
ภาพที่จะเอาไปแปะบนกระดาษต้องเป็นผังเส้นล้วน ไม่มีภาพถ่ายดาวเทียม ไม่มีพื้นดิน ไม่มีเงา จุดที่หลายคนติดคือภาพถ่ายดาวเทียมไม่ได้คุมด้วย RenderMode มันถูกแปะอยู่บนกลุ่มและเลเยอร์ชื่อกลุ่ม Terrain ต้องปิดเลเยอร์เอา
m = Sketchup.active_model
ro = m.rendering_options
v = m.active_view
c = v.camera
q = 25.4
# เก็บสภาพเดิมไว้ในตัวแปร global ก่อนแตะอะไรทั้งนั้น
$wsk ||= {
'lay' => m.layers.map { |l| [l.name, l.visible?] },
'rm' => ro['RenderMode'], 'bg' => ro['BackgroundColor'],
'gr' => ro['DrawGround'], 'ho' => ro['DrawHorizon'],
'ax' => ro['DisplaySketchAxes'],
'cam' => [c.eye.to_a, c.target.to_a, c.up.to_a, c.perspective?,
c.perspective? ? c.fov : c.height.to_f]
}
m.layers.each { |l| l.visible = false if l.name =~ /Terrain|Map |3D Bldgs|Footprints/ }
ro['RenderMode'] = 1
ro['DrawGround'] = false
ro['DrawHorizon'] = false
ro['DisplaySketchAxes'] = false
ro['BackgroundColor'] = Sketchup::Color.new(255, 255, 255)
# กล้อง top ortho: up = [1,0,0] ทำให้แกน X ของโมเดลชี้ขึ้นบนของภาพ
c.set([CX / q, CY / q, 2_000_000 / q], [CX / q, CY / q, 0], [1, 0, 0])
c.perspective = false
c.height = FRAME_H / q # ความสูงกรอบภาพ หน่วยเดียวกับโมเดล
v.refresh
v.write_image({ :filename => OUT, :width => PW, :height => PH,
:antialias => true, :transparent => false })
เส้นหนาบนโมเดลที่มีแต่เส้น
ผังที่ย่อลงมาเหลือกว้างสิบกว่าเซนติเมตรบนกระดาษ เส้นบาง ๆ จะจางจนแทบมองไม่เห็นตอนพิมพ์ ทางแก้ปกติคือเปิด DrawSilhouettes แล้วเพิ่ม SilhouetteWidth แต่มันจะไม่เกิดอะไรขึ้นเลยถ้าโมเดลไม่มีหน้า เพราะ silhouette คือขอบเงาของหน้า ไม่ใช่ของเส้น
ลำดับที่ใช้ได้คือแบบนี้ครับ
- เช็กก่อนว่าโมเดลเป็นเส้นล้วนจริงไหม เทียบจำนวน
grep(Edge).lengthกับgrep(Face).length - สร้างหน้าชั่วคราวบนวงปิดทุกวงด้วย
find_faces - ทำวัสดุโปร่งใสสนิท
alpha = 0.0แล้วใส่ทั้งmaterialและback_materialผลคือข้างในยังโปร่ง เห็นทะลุเหมือนเดิม แต่ขอบมีเงาให้ตีเส้นหนาแล้ว - เปิด
DrawSilhouettesแล้วตั้งSilhouetteWidthตามขนาดภาพ ภาพใหญ่ต้องเลขมากขึ้นตาม - เรนเดอร์ แล้วลบเฉพาะหน้าชั่วคราวทิ้ง เส้นเดิมอยู่ครบ
m.start_operation('bold outline', true)
made = []
m.entities.grep(Sketchup::Edge).each { |e| e.find_faces if e.valid? }
mt = m.materials.add('tmp_clear')
mt.alpha = 0.0 # โปร่งสนิท ไม่ใช่แค่จาง
m.entities.grep(Sketchup::Face).each { |f|
next unless f.valid?
made << f
f.material = mt
f.back_material = mt # ต้องใส่ทั้งสองด้าน ไม่งั้นทึบข้างหนึ่ง
}
ro['DrawSilhouettes'] = true
ro['SilhouetteWidth'] = 8 # ภาพ 2200 px ใช้ราว 5-14
v.refresh
v.write_image({ :filename => OUT, :width => PW, :height => PH, :antialias => true })
m.entities.erase_entities(made.select(&:valid?)) # เก็บกวาด เหลือแต่เส้นเดิม
m.commit_operation
ผมเคยโดนมาแล้ว คือระหว่างที่สคริปต์ทยอยเรนเดอร์ทีละแผ่น ลูกค้าเปิดโมเดลอยู่ด้วยแล้วสลับ view ไปมา ทำให้แผ่นที่ออกมาสภาพไม่เหมือนกัน วิธีแก้คือบังคับสภาพที่ต้องการทุกครั้งก่อนเรนเดอร์ และคืนค่าเดิมทุกตัวหลังจบ ทั้งเลเยอร์ที่ปิดไป RenderMode สีพื้นหลัง และกล้อง ตัวแปร global ของ Ruby อยู่ข้ามการเรียกคนละครั้งได้ เก็บสภาพเดิมไว้ในนั้นได้เลย
s = $wsk
if s
h = {}
s['lay'].each { |n, vis| h[n] = vis }
m.layers.each { |l| l.visible = h[l.name] if h.key?(l.name) }
ro['RenderMode'] = s['rm']
ro['BackgroundColor'] = s['bg']
ro['DrawGround'] = s['gr']
ro['DrawHorizon'] = s['ho']
ro['DisplaySketchAxes'] = s['ax']
eye, tgt, up, persp, val = s['cam']
c.set(Geom::Point3d.new(*eye), Geom::Point3d.new(*tgt), Geom::Vector3d.new(*up))
c.perspective = persp
persp ? (c.fov = val) : (c.height = val)
v.refresh
"restored rendermode=#{ro['RenderMode']}"
end
อีกข้อที่ต้องรู้คือแยกขั้นสร้างกับขั้นเรนเดอร์เป็นคนละคำสั่ง ถ้ายัด find_faces กับ write_image ไว้ก้อนเดียวกัน SketchUp ค้างได้ และภาพใหญ่ ๆ บางทีคำสั่งคืนค่ามาว่าไม่มีข้อมูล ทั้งที่ไฟล์กำลังเขียนอยู่ ให้วนดูขนาดไฟล์จนนิ่งแล้วค่อยไปอ่าน
วางเลขกำกับเองโดยไม่ให้ทับกัน
เลขกำกับอาคารต้องอยู่กลางรูปถ้ารูปใหญ่พอ ถ้ารูปเล็กก็ต้องเลื่อนออกไปข้าง ๆ แล้วลากเส้นชี้กลับเข้ามา และห้ามทับเลขตัวอื่น ห้ามทับรูปอื่น ห้ามหลุดขอบกระดาษ
วิธีที่ผมใช้ตรงไปตรงมามาก คือเรียงจากรูปใหญ่ไปเล็ก ใครมาก่อนได้ที่ก่อน แล้วลองตำแหน่งรอบ ๆ ไปเรื่อย ๆ
LW, LH, PAD = 5.0, 3.4, 0.5 # ขนาดป้ายเลข (มม.) และระยะกันชน
occupied = []
def hit(cx, cy, w, h, items, pad=0.0):
for (ox, oy, ow, oh) in items:
if abs(cx - ox) < (w + ow) / 2.0 + pad and abs(cy - oy) < (h + oh) / 2.0 + pad:
return True
return False
place = {}
for i in sorted(range(len(S)), key=lambda k: -(boxes[k][2] * boxes[k][3])):
cxm, cym, wm, hm = boxes[i]
# รูปใหญ่พอ และตรงกลางยังว่าง -> วางกลางรูปเลย
if wm >= LW + 0.8 and hm >= LH + 0.8 and not hit(cxm, cym, LW, LH, occupied, PAD):
place[i] = (cxm, cym, False)
occupied.append((cxm, cym, LW, LH))
continue
best = None
for r in (0.7, 1.0, 1.4, 1.9, 2.5, 3.2): # ไล่ออกจากรูปทีละวง
for ang in (0, 45, 315, 90, 270, 135, 225, 180): # ขวาก่อน แล้วค่อยไล่รอบ
dx = (wm / 2.0 + LW / 2.0 + 0.7) * r * math.cos(math.radians(ang))
dy = (hm / 2.0 + LH / 2.0 + 0.7) * r * math.sin(math.radians(ang))
px, py = cxm + dx, cym - dy
if px < LW / 2 or px > W - LW / 2 or py < LH / 2 or py > H - LH / 2:
continue # หลุดขอบกระดาษ
if hit(px, py, LW, LH, occupied, PAD):
continue # ทับเลขตัวอื่น
if hit(px, py, LW * 0.55, LH * 0.55,
[b for j, b in enumerate(boxes) if j != i], 0.0):
continue # ไปนั่งทับรูปอื่น
best = (px, py, True)
break
if best:
break
place[i] = best or (cxm + wm / 2.0 + LW / 2.0 + 0.8, cym, True)
occupied.append((place[i][0], place[i][1], LW, LH))
พอเลขถูกเลื่อนออกไปไกลกว่าครึ่งหนึ่งของรูป ผมลากเส้นชี้กลับเข้าไปหาจุดกลางรูป ทำด้วย div บาง ๆ หมุนตามมุมที่คำนวณได้ ไม่ต้องใช้ svg ให้ยุ่ง
เส้นบอกระยะ: อัตโนมัติเป็นหลัก แต่ต้องสั่งทับได้
ตัวเลือกด้านอัตโนมัติทำงานได้ดีประมาณแปดสิบเปอร์เซ็นต์ ที่เหลือคือกรณีที่คนดูแล้วรู้ว่าควรไปอยู่อีกด้าน แต่กติกาอัตโนมัติมองไม่เห็น ผมเลยทำช่องสั่งทับเป็นรายชิ้นไว้ ไม่ใช่ไปแก้ตรรกะกลางให้พัง
# (เลขอาคาร, ความยาวด้าน) -> (ทิศ x, ทิศ y, ระยะเยื้องต่ำสุด มม.)
DIM_EDGE_OPT = {
(9, 15.0): (0, 1, 4.0), # ด้าน 15 ม. ของอาคารชั่ง -> ดันลงล่าง
(16, 73.5): (-1, 0, 2.8), # ด้านยาวของอาคารซ่อมบำรุง -> ไปฝั่งซ้าย
(16, 26.5): (0, 1, 7.0), # ด้านกว้าง -> ลงล่างให้พ้นเลข 19
}
DIM_PREF = {18: (1, 1), 4: (-1, 1), 14: (-1, 0), 16: (0, 1)} # ทิศเส้นระยะรวมของอาคารนั้น
DIM_OFF = {18: 4.5, 4: 3.2, 9: 6.0, 16: 6.0} # ระยะเยื้องขั้นต่ำเฉพาะบางเลข
DIM_IGNORE_BUSY = {9, 5} # เลขที่ให้วางตามทิศที่สั่ง โดยไม่สนสิ่งกีดขวาง
DIM_ALL4 = {21} # เลขที่ต้องการระยะครบทั้งสี่ด้าน
NO_DIM = {6, 10, 15} # เลขที่ไม่ต้องใส่ระยะเลย
ตัวเลขในนี้เป็นค่าตัวอย่างนะครับ ประเด็นอยู่ที่โครง คือแยกกฎทั่วไปกับข้อยกเว้นออกจากกันคนละที่ เวลาลูกค้าขอแก้ "ระยะตัวนี้ย้ายไปอยู่ล่าง" ผมเติมบรรทัดเดียวในตารางแล้วรันใหม่ ไม่ต้องไปแตะโค้ดที่ทำงานถูกอยู่แล้ว
ส่วนตัวผมว่านี่คือความต่างระหว่างสคริปต์ที่ใช้ได้จริงกับสคริปต์ที่เขียนสวยแต่ต้องแก้มือทุกครั้ง
อีกเรื่องคือการย้ายเส้นระยะที่อยู่ในกลุ่ม ถ้าเลือกเก็บเส้นบอกระยะไว้ในโมเดลจริง (ไม่ใช่วาดทับตอนทำ HTML) จะเจอกับดักเดิมของ SketchUp คือกลุ่มมี transformation ของตัวเอง ใส่พิกัดโลกลงไปตรง ๆ เส้นจะไปโผล่ที่อื่น
m.start_operation("Re-place dimensions", true)
moves = { # ชื่อกลุ่ม => [ข้อความเดิม, จุดเริ่ม, จุดปลาย, ระยะเยื้อง] เมตร
"A" => ["75.00 m", [10.0, 80.0], [10.0, 155.0], [-0.8, 0.0]],
"B" => ["40.00 m", [0.0, -10.0], [0.0, 30.0], [-0.8, 0.0]],
}
log = []
moves.each do |name, (txt, a, b, off)|
g = m.entities.grep(Sketchup::Group).find { |x| x.name == name }
next log << "#{name}: ไม่พบกลุ่ม" unless g
ti = g.transformation.inverse # โลก -> local ของกลุ่มนี้
old = g.entities.grep(Sketchup::DimensionLinear).select { |d| d.text.to_s == txt }
old.each { |d| g.entities.erase_entities(d) if d.valid? }
p1 = Geom::Point3d.new(a[0].m, a[1].m, 0).transform(ti)
p2 = Geom::Point3d.new(b[0].m, b[1].m, 0).transform(ti)
nd = g.entities.add_dimension_linear(p1, p2, [off[0].m, off[1].m, 0])
log << "#{name}: #{old.size} -> #{nd.text}"
end
m.commit_operation
log.join(" | ")
ที่ผมทำเป็น "ลบตัวเดิมที่ข้อความตรงกัน แล้วสร้างใหม่" ไม่ใช่ "ขยับตัวเดิม" เพราะแบบนี้รันซ้ำกี่รอบก็ได้ผลเดิม ไม่มีเส้นซ้อนกันเป็นสิบเส้นเวลาลองปรับค่าไปมา
ธรรมเนียมการใส่ระยะที่ผมยึด: ยึดแนวศูนย์กลางเสาหรือขอบนอกอย่างใดอย่างหนึ่งให้สม่ำเสมอ ระยะที่เท่ากันหลายช่วงใส่เส้นเดียวเป็นตัวแทนพอ ไม่ต้องใส่ครบทุกช่วงเพราะรก และหน่วยกับความละเอียดตั้งให้เหมือนกันทั้งชุด
ประกอบเป็นแผ่น A4 กับ A3
ตรงนี้ผมเลือกทำเป็น HTML แล้วสั่งพิมพ์เป็น PDF ไม่ได้ทำใน LayOut เหตุผลคือควบคุมได้ทุกมิลลิเมตรจากสคริปต์ และสร้างใหม่ทั้งชุดได้ในคำสั่งเดียวเวลาผังเปลี่ยน
หัวใจคือคิดทุกอย่างเป็นมิลลิเมตรจริงบนกระดาษ ไม่ใช่ pixel แล้วเลือกมาตราส่วนจากรายการมาตรฐาน โดยหาตัวแรกที่ทำให้ผังลงกรอบพอดี
STD = [250, 500, 750, 1000, 1250, 2000, 2500, 4000, 5000]
MAXH, MAXW = 168.0, 178.0 # พื้นที่วางภาพบน A4 แนวตั้ง หลังหักขอบและหัวกระดาษ
def fit(fr):
for s in STD:
h, w = fr['hx'] / s, fr['wy'] / s # fr เป็นขนาดกรอบจริง หน่วย มม.
if h <= MAXH and w <= MAXW:
return s, w, h
s = STD[-1]
return s, fr['wy'] / s, fr['hx'] / s
# มาตราบรรทัด: เลือกความยาวที่พิมพ์แล้วยาวไม่เกิน 62 มม.
barL = 100
for L in (200, 100, 50, 25, 10):
if L * 1000.0 / sc <= 62.0:
barL = L
break
barmm = barL * 1000.0 / sc
ของที่ต้องมีครบทุกแผ่นตามที่หน่วยงานเขาดู คือหัวกระดาษ (ชื่อผู้ยื่น ที่อยู่ เลขทะเบียน) ช่องเลขที่เอกสารกับวันที่ ชื่อแบบกับหมายเลขข้อที่อ้างถึง ผังพร้อมเลขกำกับ รายการเลขกับประโยชน์ใช้สอย ลูกศรทิศเหนือ มาตราบรรทัด ข้อความบอกมาตราส่วน และช่องลงนามผู้มีอำนาจ
ทิศเหนือกับมาตราบรรทัดสำคัญกว่าที่คิด เพราะเวลาแบบถูกถ่ายเอกสารย่อ ตัวเลขมาตราส่วนที่พิมพ์ไว้จะผิดทันที แต่มาตราบรรทัดย่อตามไปด้วย เลยยังใช้วัดได้อยู่
ส่วนการทำ HTML ให้พิมพ์ออกมาหลายหน้าไม่เพี้ยน มีกับดักของมันอีกชุด ผมตั้งใจจะเขียนแยกไว้อีกบทความ เพราะเรื่องความกว้างพิมพ์จริงกับหัว-ท้ายที่ต้องซ้ำทุกหน้ามีรายละเอียดของมันเอง
ข้อสุดท้าย และเป็นข้อที่ผมพลาดมาแล้ว
งานที่ผลลัพธ์เป็นภาพ ต้องเปิดภาพขึ้นมาดูแล้วเทียบกับของอ้างอิงก่อนบอกว่าเสร็จเสมอ ผมเคยส่งงานทั้งที่ภาพที่ตัวเองเรนเดอร์ออกมาแสดงชัดว่าอาคารลอยอยู่นอกตำแหน่ง ความผิดพลาดจริงไม่ใช่การวางผิด แต่คือการบอกว่าจบทั้งที่ตัวเองเห็นแล้วว่าไม่ตรง ถ้ามันไม่ตรงให้บอกไปตรง ๆ ว่าไม่ตรงประมาณเท่าไร ดีกว่าเอาของหยาบไปเสนอว่าเสร็จแล้ว
อีกกับดักที่คู่กันคืออย่าเพิ่งคิดว่าผลที่ออกมาเรียบเกินคาดคือบั๊ก ครั้งหนึ่งผมเรนเดอร์ผังออกมาได้เป็นเส้นสะอาดบนพื้นเทา ไม่มีภาพถ่ายดาวเทียม ผมนึกว่าพัง เลยไล่แก้อยู่หลายรอบเพื่อเอาภาพดาวเทียมกลับมา สุดท้ายลูกค้าบอกว่าไม่ต้องการภาพแผนที่ ต้องการผังที่วาดชัด ๆ พอ ของที่ผมกำลังจะทิ้งคือของที่เขาต้องการอยู่แล้ว
วิธีที่ถูกคือเอาภาพให้ดูก่อนแล้วถามหนึ่งคำถาม แทนที่จะเดาแล้วเผาเวลาไปหลายรอบ
สรุป
โครงทั้งหมดคือ ดึงตัวเลขออกจากโมเดลก่อน แก้ด้านจริงของรูปที่เอียงจากพื้นที่ เรนเดอร์ top ortho โดยปิดเลเยอร์ภาพถ่ายและบังคับสภาพ view เอง ตีเส้นให้หนาด้วยหน้าโปร่งใสบวก SilhouetteWidth คืนสภาพเดิมให้เจ้าของไฟล์ทุกครั้ง แล้วปล่อยให้สคริปต์วางเลขกับเส้นระยะเองโดยมีตารางสั่งทับไว้สำหรับเคสที่ตาคนตัดสินดีกว่า
ส่วนตัวผมว่าสิ่งที่คุ้มที่สุดคือความสามารถในการสร้างใหม่ทั้งชุด ครับ ลูกค้าขอแก้ผังรอบที่หก ผมรันสามคำสั่งได้แบบชุดใหม่ครบทุกแผ่นในเวลาไม่กี่นาที เทียบกับการไล่ขยับเส้นระยะทีละเส้นในโปรแกรม ต่างกันคนละโลก
ใครจะเอาไปใช้กับงานอื่นก็ปรับตารางกับขนาดกระดาษเอาตามสะดวก ส่วนของที่หน่วยงานแต่ละแห่งต้องการไม่เหมือนกัน อันนั้นต้องไปถามเขาก่อนเสมอ อย่าเชื่อตัวอย่างในบทความรวมทั้งของผมด้วย ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ
เคยเจองานแบบนี้ไหม? ติดตรงไหน?
มาแลกวิธีกันต่อในบอร์ด LinuxThai



ความคิดเห็น 0
ยังไม่มีความคิดเห็น — เป็นคนแรกเลย