main
Caleb Publish 0.1.1 ad77cb2 18h ago
#!/usr/bin/env python3
"""Orthographic hidden-line projection of a STEP drawn over a sales-drawing view at the drawing's scale.

usage: overlay.py <step> <drawing.png> <out.png> <view> <px_per_mm> <ox_px> <oy_px> <ax_mm> <ay_mm> <crop l,t,r,b mm>
  view: one of  +x -x +y -y +z -z  (the direction the viewer LOOKS along), with an 'up' axis chosen per view.
  (ax_mm, ay_mm): the model point (in view-plane mm, see below) that lands on drawing pixel (ox_px, oy_px).
View plane coordinates: for each view the script maps model (x, y, z) to (right, up) mm:
   -y (look toward -y, side view):  right = -x, up = z      +y: right = x,  up = z
   -x (look toward -x, front view): right = y,  up = z      +x: right = -y, up = z
   -z (look down, top view):        right = x,  up = y      +z: right = -x, up = y
Visible edges red, hidden edges not drawn. Drawing lines stay black, so agreement shows as red-on-black.
"""
import sys, math
from PIL import Image, ImageDraw
import cadquery as cq
from OCP.HLRBRep import HLRBRep_Algo, HLRBRep_HLRToShape
from OCP.HLRAlgo import HLRAlgo_Projector
from OCP.gp import gp_Ax2, gp_Pnt, gp_Dir
from OCP.BRepAdaptor import BRepAdaptor_Curve
from OCP.GCPnts import GCPnts_QuasiUniformDeflection
from OCP.TopExp import TopExp_Explorer
from OCP.TopAbs import TopAbs_EDGE
from OCP.TopoDS import TopoDS

VIEWS = {  # view dir (toward which the viewer looks), x-dir of the projection = 'right'
    '-y': ((0, -1, 0), (-1, 0, 0)), '+y': ((0, 1, 0), (1, 0, 0)),
    '-x': ((-1, 0, 0), (0, 1, 0)), '+x': ((1, 0, 0), (0, -1, 0)),
    '-z': ((0, 0, -1), (1, 0, 0)), '+z': ((0, 0, 1), (-1, 0, 0)),
}


def project(shape, view):
    d, xd = VIEWS[view]
    # HLRAlgo projector looks along -N, so give N = -viewdir
    ax = gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(-d[0], -d[1], -d[2]), gp_Dir(*xd))
    algo = HLRBRep_Algo(); algo.Add(shape); algo.Projector(HLRAlgo_Projector(ax)); algo.Update(); algo.Hide()
    h = HLRBRep_HLRToShape(algo)
    segs = []
    for comp in (h.VCompound(), h.OutLineVCompound(), h.Rg1LineVCompound()):
        if comp is None or comp.IsNull(): continue
        ex = TopExp_Explorer(comp, TopAbs_EDGE)
        while ex.More():
            e = TopoDS.Edge_s(ex.Current()); c = BRepAdaptor_Curve(e)
            pts = GCPnts_QuasiUniformDeflection(c, 0.005)
            if pts.IsDone():
                segs.append([(pts.Value(i).X(), pts.Value(i).Y()) for i in range(1, pts.NbPoints() + 1)])
            ex.Next()
    return segs


def main(a):
    step, img, out, view = a[0], a[1], a[2], a[3]
    s, ox, oy, axm, aym = map(float, a[4:9])
    l, t, r, b = map(float, a[9].split(','))
    shape = cq.importers.importStep(step).val().wrapped
    segs = project(shape, view)
    im = Image.open(img).convert('RGB')
    # crop window in model mm around the anchor
    L, T = int(ox + (l - axm) * s), int(oy - (t - aym) * s)
    R, B = int(ox + (r - axm) * s), int(oy - (b - aym) * s)
    im = im.crop((L, T, R, B))
    up = 2
    im = im.resize((im.width * up, im.height * up), Image.LANCZOS)
    dr = ImageDraw.Draw(im)
    for seg in segs:
        pts = [((ox + (x - axm) * s - L) * up, (oy - (y - aym) * s - T) * up) for x, y in seg]
        if len(pts) > 1: dr.line(pts, fill=(230, 0, 0), width=2)
    im.save(out)
    print(out, im.size, len(segs), 'edges')


if __name__ == '__main__':
    main(sys.argv[1:])