main
John Lauer STM32G431CBT6: datasheet-built LQFP48 model (DS12589 Rev 2 Table 94) with ST's real top marking STM32G431 / CBT6 / Y WW… 751ee87
#!/usr/bin/env python3
"""Add extra marking pieces (date code, revision code) to an adom-aiflow-etch result, in the same
Adom.LaserEtch group, flat on the same plane, using the same /laser-etch service glyphs.
Layout is given in the mark's reading frame (r along the reading direction, up = text up), mm,
relative to the centre of the existing mark block.
usage: extra_marks.py etched.step out.step spec.json
spec: {"align_line2": "left"|"center", "shift_up": 0.0,
       "pieces": [{"text": "Y WW", "cap": 0.5, "anchor": "right"|"left"|"center", "r": 2.5, "up": -1.3}]}
  r/up = position of the piece's anchor edge (r) and its vertical centre (up), after shift_up."""
import sys, json, os, tempfile
sys.path.insert(0, "/home/adom/project/adom_modules/adom/adom-aiflow/tools/laser-etch")
import step_text as ST, mark_geom as MG, mark_plan as MP, mark_svc as SVC
from mark_etch import PROXY
src, out, spec = sys.argv[1], sys.argv[2], json.load(open(sys.argv[3]))
A = ST.Step(src); A.flatten()
m = MG.analyse(A, "Adom.LaserEtch")
u, v, z = m["u"], m["v"], m["z"]
def to_xy(r, up):
    return (r * u[0] + up * v[0], r * u[1] + up * v[1])
def rf(p):   # board xy -> reading frame
    return (p[0] * u[0] + p[1] * u[1], p[0] * v[0] + p[1] * v[1])
bb = m["bbox"]
corners = [rf((bb[0], bb[1])), rf((bb[3], bb[4]))]
r0, r1 = sorted([corners[0][0], corners[1][0]]); u0, u1 = sorted([corners[0][1], corners[1][1]])
rc, uc = (r0 + r1) / 2, (u0 + u1) / 2
report = dict(u=u, v=v, z=z, block_r=[r0, r1], block_up=[u0, u1], lines=[])
def line_rf(l):
    b = l["box"]; a = rf((b[0], b[1])); c = rf((b[2], b[3]))
    return sorted([a[0], c[0]]), sorted([a[1], c[1]])
if spec.get("align_line2") == "left" and len(m["lines"]) == 2:
    l1r, _ = line_rf(m["lines"][0]); l2r, _ = line_rf(m["lines"][1])
    dr = l1r[0] - l2r[0]
    ents = A.faces_subset_entities("Adom.LaserEtch", m["lines"][1]["faces"])
    dx, dy = to_xy(dr, 0)
    A.transform_entities(ents, (0, 0, 0), 1.0, (dx, dy, 0))
su = spec.get("shift_up", 0.0)
if "block_up_centre" in spec:
    su = spec["block_up_centre"] - uc
if su:
    dx, dy = to_xy(0, su)
    A.transform_product("Adom.LaserEtch", (0, 0, z), 1.0, 0, (dx, dy, 0), set_z=z)
tmp = tempfile.mkdtemp()
A.write(os.path.join(tmp, "base.step"))
piece_files = []
last_q = 0
for i, p in enumerate(spec["pieces"]):
    proxy = ST.Step(PROXY).scale_axes(-5, -2, -0.05, 10, 4, 0.05)
    e = ST.Step(SVC.laser_etch(proxy.text, p["text"], None, job="extra-mark"))
    e.flatten()
    me = MG.analyse(e, "Adom.LaserEtch")
    if p.get("turn_like_previous") and i > 0:
        q = last_q
    else:
        q = MP.quarter_turns_to(me["u"], (u[0], u[1]))
    last_q = q
    q = (q + p.get("extra_q", 0)) % 4
    e.transform_product("Adom.LaserEtch", me["centre"], 1.0, q)
    me = MG.analyse(e, "Adom.LaserEtch")
    k = p["cap"] / me["capMm"]
    e.transform_product("Adom.LaserEtch", me["centre"], k, 0)
    me = MG.analyse(e, "Adom.LaserEtch")
    b = me["bbox"]; a = rf((b[0], b[1])); c = rf((b[3], b[4]))
    pr0, pr1 = sorted([a[0], c[0]]); pu0, pu1 = sorted([a[1], c[1]])
    anchor = p.get("anchor", "center")
    cur_r = {"left": pr0, "right": pr1, "center": (pr0 + pr1) / 2}[anchor]
    if spec.get("absolute"):
        tr, tu = p["r"] - cur_r, p["up"] - (pu0 + pu1) / 2
    else:
        tr, tu = rc + p["r"] - cur_r, uc + su + p["up"] - (pu0 + pu1) / 2
    dx, dy = to_xy(tr, tu)
    e.transform_product("Adom.LaserEtch", (0, 0, 0), 1.0, 0, (dx, dy, 0), set_z=z)
    f = os.path.join(tmp, "piece%d.step" % i); e.write(f); piece_files.append(f)
    report["lines"].append(dict(text=p["text"], cap=p["cap"], r=[pr0 + tr, pr1 + tr], up=[pu0 + tu, pu1 + tu]))
# merge with XCAF
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_LabelSequence, TDF_Label
from OCP.TDataStd import TDataStd_Name
from OCP.TopoDS import TopoDS_Compound
from OCP.BRep import BRep_Builder
from OCP.TopExp import TopExp_Explorer
from OCP.TopAbs import TopAbs_FACE
from OCP.Quantity import Quantity_Color
from OCP.Interface import Interface_Static
def read(f):
    d = TDocStd_Document(TCollection_ExtendedString("XmlOcaf"))
    r = STEPCAFControl_Reader(); r.SetColorMode(True); r.SetNameMode(True); r.ReadFile(f); r.Transfer(d)
    return d
def nm(l):
    n = TDataStd_Name(); return n.Get().ToExtString() if l.FindAttribute(TDataStd_Name.GetID_s(), n) else ""
def find(st, want):
    seq = TDF_LabelSequence(); st.GetShapes(seq)
    for i in range(1, seq.Length() + 1):
        if nm(seq.Value(i)) == want and not st.IsAssembly_s(seq.Value(i)):
            return seq.Value(i)
doc = read(os.path.join(tmp, "base.step"))
st = XCAFDoc_DocumentTool.ShapeTool_s(doc.Main()); ct = XCAFDoc_DocumentTool.ColorTool_s(doc.Main())
lab = find(st, "Adom.LaserEtch")
assert lab is not None, "no Adom.LaserEtch part"
col = Quantity_Color()
got = ct.GetColor_s(lab, XCAFDoc_ColorType.XCAFDoc_ColorSurf, col) or ct.GetColor_s(lab, XCAFDoc_ColorType.XCAFDoc_ColorGen, col) or ct.GetColor(st.GetShape_s(lab), XCAFDoc_ColorType.XCAFDoc_ColorSurf, col)
if not got:
    ex = TopExp_Explorer(st.GetShape_s(lab), TopAbs_FACE)
    while ex.More():
        if ct.GetColor(ex.Current(), XCAFDoc_ColorType.XCAFDoc_ColorSurf, col): got = True; break
        ex.Next()
comp = TopoDS_Compound(); bld = BRep_Builder(); bld.MakeCompound(comp)
bld.Add(comp, st.GetShape_s(lab))
for f in piece_files:
    d2 = read(f); st2 = XCAFDoc_DocumentTool.ShapeTool_s(d2.Main())
    l2 = find(st2, "Adom.LaserEtch")
    bld.Add(comp, st2.GetShape_s(l2))
st.SetShape(lab, comp)
st.UpdateAssemblies()
if got:
    ct.SetColor(lab, col, XCAFDoc_ColorType.XCAFDoc_ColorSurf)
    ex = TopExp_Explorer(comp, TopAbs_FACE)
    while ex.More():
        ct.SetColor(ex.Current(), col, XCAFDoc_ColorType.XCAFDoc_ColorSurf); ex.Next()
Interface_Static.SetCVal_s("write.step.schema", "AP214IS")
w = STEPCAFControl_Writer(); w.SetColorMode(True); w.SetNameMode(True)
w.Transfer(doc, STEPControl_AsIs); w.Write(out)
report["colour"] = [col.Red(), col.Green(), col.Blue()] if got else None
print(json.dumps(report, indent=1))