"""Hand-drawn, pressure-varied Hanja pigment on the existing stone atlas; no fonts.""" import bpy,numpy as np,math s=bpy.data.scenes['Summit slab and marker'];bpy.context.window.scene=s body=bpy.data.objects['Photo matched summit boulder'];mat=body.data.materials[0];nodes=mat.node_tree.nodes;links=mat.node_tree.links N=2048;mask=np.zeros((N,N),dtype=np.float32) # Each control point is x, y (down), brush width. Sweeps and hooked ends are individually drawn. 冠=[[(.13,.17,.045),(.10,.26,.078),(.08,.34,.018)],[(.12,.20,.035),(.48,.17,.062),(.87,.14,.085),(.83,.30,.015)],[(.22,.38,.025),(.39,.35,.055),(.54,.36,.018)],[(.16,.53,.022),(.35,.49,.065),(.56,.48,.027)],[(.29,.53,.072),(.26,.70,.052),(.18,.81,.032),(.11,.85,.007)],[(.45,.51,.073),(.42,.72,.062),(.46,.87,.047),(.66,.88,.066),(.87,.83,.076),(.90,.69,.010)],[(.57,.43,.025),(.74,.41,.055),(.91,.40,.012)],[(.77,.29,.032),(.78,.47,.069),(.76,.69,.048),(.69,.73,.014)],[(.59,.54,.024),(.65,.58,.070),(.67,.62,.012)]] 岳=[[(.73,.10,.025),(.52,.16,.072),(.29,.21,.027)],[(.28,.22,.074),(.26,.40,.063),(.24,.56,.036)],[(.30,.35,.020),(.50,.34,.057),(.72,.31,.021)],[(.68,.34,.059),(.66,.46,.068),(.65,.56,.027)],[(.12,.58,.017),(.43,.56,.066),(.86,.54,.020)],[(.49,.64,.035),(.50,.77,.080),(.48,.91,.037)],[(.22,.72,.035),(.23,.91,.072),(.45,.92,.049),(.78,.88,.044)],[(.79,.68,.029),(.80,.77,.074),(.77,.93,.018)]] 山=[[(.49,.12,.024),(.50,.32,.083),(.48,.56,.069),(.49,.82,.045)],[(.18,.39,.035),(.19,.62,.078),(.20,.86,.072),(.43,.85,.043),(.80,.81,.063)],[(.82,.34,.032),(.81,.56,.083),(.80,.78,.061),(.76,.87,.012)]] def brush(points,cx,top,w,h,lean,seed): rng=np.random.default_rng(seed) controls=np.array(points) # Catmull-Rom interpolation produces continuous hand motion rather than straight font stems. padded=np.vstack([controls[0],controls,controls[-1]]) samples=[] for i in range(1,len(padded)-2): a,b,c,d=padded[i-1:i+3] for t in np.linspace(0,1,28,endpoint=False): samples.append(.5*((2*b)+(-a+c)*t+(2*a-5*b+4*c-d)*t*t+(-a+3*b-3*c+d)*t*t*t)) samples.append(controls[-1]) for k,(x,y,width) in enumerate(samples): px=cx+(x-.5)*w+lean*(.5-y);pz=top-y*h u=(.02+.46*(px+1.3)/2.6)*(N-1);v=(.02+.96*(pz+.2)/2.3)*(N-1) rx=max(.45,width*w*.46/2.6*N*.5);ry=max(.6,width*h*.96/2.3*N*.5) rx*=1+.08*math.sin(k*.73+seed) x0=max(0,int(u-rx-2));x1=min(N,int(u+rx+3));y0=max(0,int(v-ry-2));y1=min(N,int(v+ry+3)) X,Y=np.meshgrid(np.arange(x0,x1)+.5,np.arange(y0,y1)+.5) radius=np.sqrt(((X-u)/rx)**2+((Y-v)/ry)**2) dab=np.clip((1.10-radius)*5,0,1) mask[y0:y1,x0:x1]=np.maximum(mask[y0:y1,x0:x1],dab) for char,cx,top,w,h,lean,seed in [(冠,.18,1.78,.48,.45,.025,13),(岳,.15,1.29,.43,.41,-.012,31),(山,.20,.83,.47,.40,.018,58)]: for i,stroke in enumerate(char):brush(stroke,cx,top,w,h,lean,seed+i*7) # Small mineral-shaped losses, sparse chips and uneven pigment opacity; retain legibility. rng=np.random.default_rng(194);Y,X=np.indices(mask.shape) wear=.89+.07*np.sin(X*.19+np.sin(Y*.27))+.035*rng.random(mask.shape) chips=rng.random(mask.shape)<.025 mask*=np.where(chips,.12,wear) for _ in range(55): occupied=np.argwhere(mask>.7) y,x=occupied[rng.integers(len(occupied))];rx=rng.uniform(.6,1.7);ry=rng.uniform(.8,2.5) x0=max(0,x-4);x1=min(N,x+5);y0=max(0,y-5);y1=min(N,y+6) yy,xx=np.indices((y1-y0,x1-x0));patch=((xx+x0-x)/rx)**2+((yy+y0-y)/ry)**2<1 mask[y0:y1,x0:x1]*=np.where(patch,.25,1) image_mask=bpy.data.images['Hanja pigment mask'];rgba=np.ones((N,N,4),dtype=np.float32);rgba[:,:,:3]=mask[:,:,None] image_mask.pixels.foreach_set(rgba.ravel());image_mask.update();image_mask.pack() bs=next(n for n in nodes if n.type=='BSDF_PRINCIPLED') paint=next(n for n in nodes if n.type=='MIX_RGB' and n.inputs[0].is_linked) target=next(n for n in nodes if n.type=='TEX_IMAGE' and n.image and n.image.name=='Painted Hanja on granite') links.new(paint.outputs[0],bs.inputs['Base Color']);nodes.active=target for o in s.objects:o.select_set(False) body.select_set(True);bpy.context.view_layer.objects.active=body s.render.bake.use_pass_direct=False;s.render.bake.use_pass_indirect=False;s.render.bake.use_pass_color=True bpy.ops.object.bake(type=next(i.identifier for i in bpy.ops.object.bake.get_rna_type().properties['type'].enum_items if i.identifier=='DIFFUSE')) target.image.save();target.image.pack();links.new(target.outputs['Color'],bs.inputs['Base Color']) body['inscriptionStyle']='Hand-drawn brush paths, pressure variation and worn pigment; no font rasterization' s.render.filepath='D:/project/sunrise/assets/summit/blender-plateau-study/marker-brush-painted.png' bpy.ops.render.render(write_still=True) print('Painted 20 hand-drawn brush paths with tapered hooks and sparse pigment wear.')