master
John Lauer MPN laser etch on the side face (window kept clear) cde8bd5 20d ago
#!/usr/bin/env python3
"""Measure the flat laser_etch mark in a merged STEP and move it (in X/Y only) so its centre sits on the centre
of a region (the side face chosen by side_region.py). Prints JSON with the mark size before the move and the
scale that would make it fill the region with the given margins (etch_part.py re-asks the service with frac
x scale). Colours and names kept (XCAF).
Usage: fit_mark.py in.step out.step x0,y0,x1,y1 [margin_w=0.10] [margin_h=0.18]"""
import sys, json
from OCP.STEPCAFControl import STEPCAFControl_Reader, STEPCAFControl_Writer
from OCP.STEPControl import STEPControl_AsIs
from OCP.TDocStd import TDocStd_Document
from OCP.TCollection import TCollection_ExtendedString
from OCP.XCAFDoc import XCAFDoc_DocumentTool, XCAFDoc_ColorType
from OCP.TDF import TDF_ChildIterator, TDF_Label
from OCP.TDataStd import TDataStd_Name
from OCP.TopLoc import TopLoc_Location
from OCP.Bnd import Bnd_Box
from OCP.BRepBndLib import BRepBndLib
from OCP.gp import gp_Trsf, gp_Vec
from OCP.BRepBuilderAPI import BRepBuilderAPI_Transform
from OCP.Quantity import Quantity_Color
from OCP.TopExp import TopExp_Explorer
from OCP.TopAbs import TopAbs_FACE
src, out = sys.argv[1:3]; x0, y0, x1, y1 = map(float, sys.argv[3].split(','))
mw = float(sys.argv[4]) if len(sys.argv) > 4 else 0.10; mh = float(sys.argv[5]) if len(sys.argv) > 5 else 0.18
doc = TDocStd_Document(TCollection_ExtendedString("d"))
r = STEPCAFControl_Reader(); r.SetColorMode(True); r.SetNameMode(True); r.SetLayerMode(True); r.ReadFile(src); r.Transfer(doc)
st = XCAFDoc_DocumentTool.ShapeTool_s(doc.Main()); ct = XCAFDoc_DocumentTool.ColorTool_s(doc.Main())
def name(l):
    nm = TDataStd_Name(); return nm.Get().ToExtString() if l.FindAttribute(TDataStd_Name.GetID_s(), nm) else ''
found = []
def walk(l, loc, named):
    if st.IsReference_s(l):
        ref = TDF_Label(); st.GetReferredShape_s(l, ref); return walk(ref, loc.Multiplied(st.GetLocation_s(l)), named or name(l) == 'laser_etch')
    if st.IsAssembly_s(l):
        it = TDF_ChildIterator(l, False)
        while it.More(): c = it.Value(); it.Next(); walk(c, loc, named)
        return
    if named or name(l) == 'laser_etch': found.append((l, loc))
it = TDF_ChildIterator(st.Label(), False)
while it.More():
    l = it.Value(); it.Next()
    if st.IsFree_s(l) and st.IsTopLevel(l): walk(l, TopLoc_Location(), False)
assert len(found) == 1, len(found); ml, mloc = found[0]
b = Bnd_Box(); BRepBndLib.Add_s(st.GetShape_s(ml).Moved(mloc), b); mn, mx = b.CornerMin(), b.CornerMax()
w, h = mx.X() - mn.X(), mx.Y() - mn.Y(); rw, rh = x1 - x0, y1 - y0
scale = min(rw * (1 - 2 * mw) / w, rh * (1 - 2 * mh) / h)
dx = (x0 + x1) / 2 - (mn.X() + mx.X()) / 2; dy = (y0 + y1) / 2 - (mn.Y() + mx.Y()) / 2
v = gp_Vec(dx, dy, 0).Transformed(mloc.Transformation().Inverted()); t = gp_Trsf(); t.SetTranslation(v)
col = None                                         # the mark colour lives on the label or (as read back from STEP) on its faces
for tt in (XCAFDoc_ColorType.XCAFDoc_ColorSurf, XCAFDoc_ColorType.XCAFDoc_ColorGen):
    c = Quantity_Color()
    if col is None and ct.GetColor_s(ml, tt, c): col = c
ex = TopExp_Explorer(st.GetShape_s(ml), TopAbs_FACE)
while col is None and ex.More():
    c = Quantity_Color()
    if ct.GetColor(ex.Current(), XCAFDoc_ColorType.XCAFDoc_ColorSurf, c) or ct.GetColor(ex.Current(), XCAFDoc_ColorType.XCAFDoc_ColorGen, c): col = c
    ex.Next()
assert col is not None, 'mark colour not found'
st.SetShape(ml, BRepBuilderAPI_Transform(st.GetShape_s(ml), t, True).Shape())
if True:
    ct.SetColor(ml, col, XCAFDoc_ColorType.XCAFDoc_ColorSurf); ct.SetColor(ml, col, XCAFDoc_ColorType.XCAFDoc_ColorGen)
    ex = TopExp_Explorer(st.GetShape_s(ml), TopAbs_FACE)
    while ex.More():
        sub = st.AddSubShape(ml, ex.Current()); ex.Next()
        if not sub.IsNull(): ct.SetColor(sub, col, XCAFDoc_ColorType.XCAFDoc_ColorSurf)
st.UpdateAssemblies()
wr = STEPCAFControl_Writer(); wr.SetColorMode(True); wr.SetNameMode(True); wr.SetLayerMode(True); wr.Transfer(doc, STEPControl_AsIs); wr.Write(out)
print(json.dumps(dict(mark_mm=[round(w, 4), round(h, 4)], region_mm=[round(rw, 4), round(rh, 4)], scale_to_fill=round(scale, 4),
                      moved_mm=[round(dx, 4), round(dy, 4)], mark_region=[round(mn.X() + dx, 4), round(mn.Y() + dy, 4), round(mx.X() + dx, 4), round(mx.Y() + dy, 4)])))