*/}}
Browse Source

slender etc.

Yiming Wu 1 year ago
parent
commit
299d9101b7
4 changed files with 107 additions and 23 deletions
  1. 42 8
      ournodes.c
  2. 40 11
      ouroperations.c
  3. 1 1
      ourpaint.c
  4. 24 3
      ourpaint.h

+ 42 - 8
ournodes.c

@@ -13,18 +13,21 @@ laPropContainer* OUR_PC_IDN_BRUSH_OUTPUTS;
 laPropContainer* OUR_PC_IDN_BRUSH_DEVICE;
 
 void IDN_BrushSettingsInit(OurBrushSettingsNode* n){
+    n->CanvasScale=laCreateOutSocket(n,"Canvas Scale",LA_PROP_FLOAT);   n->CanvasScale->Data=&n->rCanvasScale;
     n->Size=laCreateOutSocket(n,"Size",LA_PROP_FLOAT);                  n->Size->Data=&n->rSize;
     n->Transparency=laCreateOutSocket(n,"Transparency",LA_PROP_FLOAT);  n->Transparency->Data=&n->rTransparency;
     n->Hardness=laCreateOutSocket(n,"Hardness",LA_PROP_FLOAT);          n->Hardness->Data=&n->rHardness;
     n->Smudge=laCreateOutSocket(n,"Smudge",LA_PROP_FLOAT);              n->Smudge->Data=&n->rSmudge;
     n->SmudgeLength=laCreateOutSocket(n,"Smudge Length",LA_PROP_FLOAT); n->SmudgeLength->Data=&n->rSmudgeLength;
     n->DabsPerSize=laCreateOutSocket(n,"Dabs Per Size",LA_PROP_FLOAT);  n->DabsPerSize->Data=&n->rDabsPerSize;
+    n->Slender=laCreateOutSocket(n,"Slender",LA_PROP_FLOAT);            n->Slender->Data=&n->rSlender;
+    n->Angle=laCreateOutSocket(n,"Angle",LA_PROP_FLOAT);                n->Angle->Data=&n->rAngle;
     strSafeSet(&n->Base.Name, "Brush Settings");
 }
 void IDN_BrushSettingsDestroy(OurBrushSettingsNode* n){
-    laDestroyOutSocket(n->Size); laDestroyOutSocket(n->Transparency); laDestroyOutSocket(n->Hardness);
-    laDestroyOutSocket(n->Smudge); laDestroyOutSocket(n->SmudgeLength); laDestroyOutSocket(n->DabsPerSize);
-    strSafeDestroy(&n->Base.Name);
+    laDestroyOutSocket(n->Size); laDestroyOutSocket(n->Transparency); laDestroyOutSocket(n->Hardness); laDestroyOutSocket(n->Smudge);
+    laDestroyOutSocket(n->SmudgeLength); laDestroyOutSocket(n->DabsPerSize); laDestroyOutSocket(n->Slender); laDestroyOutSocket(n->Angle);
+    laDestroyOutSocket(n->CanvasScale); strSafeDestroy(&n->Base.Name);
 }
 int IDN_BrushSettingsVisit(OurBrushSettingsNode* n, laListHandle* l){
     LA_GUARD_THIS_NODE(n); n->Base.Eval=LA_DAG_FLAG_PERM; lstAppendPointer(l, n);
@@ -32,52 +35,64 @@ int IDN_BrushSettingsVisit(OurBrushSettingsNode* n, laListHandle* l){
 }
 int IDN_BrushSettingsEval(OurBrushSettingsNode* n){
     if(!Our->CurrentBrush){ return 0; } // unlikely;
+    n->rCanvasScale = Our->CurrentScale;
     n->rSize = Our->CurrentBrush->Size;
     n->rTransparency = Our->CurrentBrush->Transparency;
     n->rHardness = Our->CurrentBrush->Hardness;
     n->rSmudge = Our->CurrentBrush->Smudge;
     n->rSmudgeLength = Our->CurrentBrush->SmudgeResampleLength;
     n->rDabsPerSize = Our->CurrentBrush->DabsPerSize;
+    n->rSlender = Our->CurrentBrush->Slender;
+    n->rAngle = Our->CurrentBrush->Angle;
     return 1;
 }
 void ui_BrushSettingsNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); OurBrushSettingsNode*n=This->EndInstance;
     laUiItem* b,*u;
     LA_BASE_NODE_HEADER(uil,c,This);
-
+    
+    b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Canvas Scale",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;  laShowNodeSocket(uil,c,This,"canvas_scale",0);  laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Size",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;          laShowNodeSocket(uil,c,This,"size",0);          laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Transparency",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;  laShowNodeSocket(uil,c,This,"transparency",0);  laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Hardness",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;      laShowNodeSocket(uil,c,This,"hardness",0);      laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Smudge",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;        laShowNodeSocket(uil,c,This,"smudge",0);        laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Smudge Length",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1; laShowNodeSocket(uil,c,This,"smudge_length",0); laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Dabs Per Size",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1; laShowNodeSocket(uil,c,This,"dabs_per_size",0); laEndRow(uil,b);
-
+    b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Slender",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;       laShowNodeSocket(uil,c,This,"slender",0);       laEndRow(uil,b);
+    b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Angle",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;         laShowNodeSocket(uil,c,This,"angle",0);         laEndRow(uil,b);
 }
 
 void IDN_BrushOutputsInit(OurBrushOutputsNode* n){
+    n->Position=laCreateInSocket("Position",LA_PROP_FLOAT);
     n->Size=laCreateInSocket("Size",LA_PROP_FLOAT);
     n->Transparency=laCreateInSocket("Transparency",LA_PROP_FLOAT);
     n->Hardness=laCreateInSocket("Hardness",LA_PROP_FLOAT);
     n->Smudge=laCreateInSocket("Smudge",LA_PROP_FLOAT);
     n->SmudgeLength=laCreateInSocket("Smudge Length",LA_PROP_FLOAT);
     n->DabsPerSize=laCreateInSocket("Dabs Per Size",LA_PROP_FLOAT);
+    n->Slender=laCreateInSocket("Slender",LA_PROP_FLOAT);
+    n->Angle=laCreateInSocket("Angle",LA_PROP_FLOAT);
     strSafeSet(&n->Base.Name, "Brush Outputs");
 }
 void IDN_BrushOutputsDestroy(OurBrushOutputsNode* n){
-    laDestroyOutSocket(n->Size); laDestroyOutSocket(n->Transparency); laDestroyOutSocket(n->Hardness);
-    laDestroyOutSocket(n->Smudge); laDestroyOutSocket(n->SmudgeLength); laDestroyOutSocket(n->DabsPerSize);
+    laDestroyInSocket(n->Position);
+    laDestroyInSocket(n->Size); laDestroyInSocket(n->Transparency); laDestroyInSocket(n->Hardness); laDestroyInSocket(n->Smudge);
+    laDestroyInSocket(n->SmudgeLength); laDestroyInSocket(n->DabsPerSize); laDestroyInSocket(n->Slender); laDestroyInSocket(n->Angle);
     strSafeDestroy(&n->Base.Name);
 }
 int IDN_BrushOutputsVisit(OurBrushOutputsNode* n, laListHandle* l){
     LA_GUARD_THIS_NODE(n);
 #define BRUSH_OUT_VISIT(a)\
     if(LA_SRC_AND_PARENT(n->a)){ laBaseNode*bn=n->a->Source->Parent; LA_VISIT_NODE(bn); }
+    BRUSH_OUT_VISIT(Position)
     BRUSH_OUT_VISIT(Size)
     BRUSH_OUT_VISIT(Transparency)
     BRUSH_OUT_VISIT(Hardness)
     BRUSH_OUT_VISIT(Smudge)
     BRUSH_OUT_VISIT(SmudgeLength)
     BRUSH_OUT_VISIT(DabsPerSize)
+    BRUSH_OUT_VISIT(Slender)
+    BRUSH_OUT_VISIT(Angle)
 #undef BRUSH_OUT_VISIT
     n->Base.Eval=LA_DAG_FLAG_PERM; lstAppendPointer(l, n);
     return LA_DAG_FLAG_PERM;
@@ -86,12 +101,18 @@ int IDN_BrushOutputsEval(OurBrushOutputsNode* n){
     if(!Our->CurrentBrush) return 0;
 #define BRUSH_OUT_EVAL(a)\
     if(LA_SRC_AND_PARENT(n->a) && (n->a->Source->DataType&LA_PROP_FLOAT)){ Our->CurrentBrush->Eval##a=*((real*)n->a->Source->Data); }
+    if(LA_SRC_AND_PARENT(n->Position) && (n->Position->Source->DataType&LA_PROP_FLOAT|LA_PROP_ARRAY) && n->Position->Source->ArrLen>=2){
+        Our->CurrentBrush->EvalPositionOut[0]=((real*)n->Position->Source->Data)[0];
+        Our->CurrentBrush->EvalPositionOut[1]=((real*)n->Position->Source->Data)[1];
+    }
     BRUSH_OUT_EVAL(Size)
     BRUSH_OUT_EVAL(Transparency)
     BRUSH_OUT_EVAL(Hardness)
     BRUSH_OUT_EVAL(Smudge)
     BRUSH_OUT_EVAL(SmudgeLength)
     BRUSH_OUT_EVAL(DabsPerSize)
+    BRUSH_OUT_EVAL(Slender)
+    BRUSH_OUT_EVAL(Angle)
 #undef BRUSH_OUT_EVAL
     return 1;
 }
@@ -100,12 +121,15 @@ void ui_BrushOutputsNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laC
     laUiItem* b,*u;
     LA_BASE_NODE_HEADER(uil,c,This);
 
+    b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"position",0);      laShowLabel(uil,c,"Position",0,0);      laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"size",0);          laShowLabel(uil,c,"Size",0,0);          laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"transparency",0);  laShowLabel(uil,c,"Transparency",0,0);  laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"hardness",0);      laShowLabel(uil,c,"Hardness",0,0);      laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"smudge",0);        laShowLabel(uil,c,"Smudge",0,0);        laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"smudge_length",0); laShowLabel(uil,c,"Smudge Length",0,0); laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"dabs_per_size",0); laShowLabel(uil,c,"Dabs Per Size",0,0); laEndRow(uil,b);
+    b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"slender",0);       laShowLabel(uil,c,"Slender",0,0);       laEndRow(uil,b);
+    b=laBeginRow(uil,c,0,0); laShowNodeSocket(uil,c,This,"angle",0);         laShowLabel(uil,c,"Angle",0,0);         laEndRow(uil,b);
 }
 
 void IDN_BrushDeviceInit(OurBrushDeviceNode* n){
@@ -113,10 +137,11 @@ void IDN_BrushDeviceInit(OurBrushDeviceNode* n){
     n->Position=laCreateOutSocket(n,"Position",LA_PROP_FLOAT|LA_PROP_ARRAY); n->Position->Data=n->rPosition; n->Position->ArrLen=2;
     n->Tilt=laCreateOutSocket(n,"Tilt",LA_PROP_FLOAT|LA_PROP_ARRAY);         n->Tilt->Data=n->rTilt; n->Tilt->ArrLen=2;
     n->IsEraser=laCreateOutSocket(n,"Is Eraser",LA_PROP_INT);                n->IsEraser->Data=&n->rIsEraser;
+    n->LastPosition=laCreateOutSocket(n,"Last Position",LA_PROP_FLOAT|LA_PROP_ARRAY); n->LastPosition->Data=n->rLastPosition; n->LastPosition->ArrLen=2;
     strSafeSet(&n->Base.Name, "Brush Device");
 }
 void IDN_BrushDeviceDestroy(OurBrushDeviceNode* n){
-    laDestroyOutSocket(n->Pressure); laDestroyOutSocket(n->Tilt); laDestroyOutSocket(n->Position); laDestroyOutSocket(n->IsEraser);
+    laDestroyOutSocket(n->Pressure); laDestroyOutSocket(n->Tilt); laDestroyOutSocket(n->Position); laDestroyOutSocket(n->IsEraser); laDestroyOutSocket(n->LastPosition);
     strSafeDestroy(&n->Base.Name);
 }
 int IDN_BrushDeviceVisit(OurBrushDeviceNode* n, laListHandle* l){
@@ -126,6 +151,7 @@ int IDN_BrushDeviceVisit(OurBrushDeviceNode* n, laListHandle* l){
 int IDN_BrushDeviceEval(OurBrushDeviceNode* n){
     if(!Our->CurrentBrush){ return 0; } // unlikely;
     tnsVectorSet2v(n->rPosition, Our->CurrentBrush->EvalPosition);
+    tnsVectorSet2(n->rLastPosition, Our->CurrentBrush->LastX, Our->CurrentBrush->LastY); printf("%lf %lf\n",Our->CurrentBrush->LastX, Our->CurrentBrush->LastY);
     tnsVectorSet2v(n->rTilt, Our->CurrentBrush->EvalTilt);
     n->rIsEraser = Our->CurrentBrush->EvalIsEraser;
     n->rPressure = Our->CurrentBrush->EvalPressure;
@@ -137,6 +163,7 @@ void ui_BrushDeviceNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laCo
     LA_BASE_NODE_HEADER(uil,c,This);
 
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Position",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1; laShowNodeSocket(uil,c,This,"position",0);  laEndRow(uil,b);
+    b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Last Position",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1; laShowNodeSocket(uil,c,This,"last_position",0);  laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Pressure",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1; laShowNodeSocket(uil,c,This,"pressure",0);  laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Tilt",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;     laShowNodeSocket(uil,c,This,"tilt",0);      laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); u=laShowLabel(uil,c,"Is Eraser",0,0);u->Flags|=LA_TEXT_ALIGN_RIGHT; u->Expand=1;laShowNodeSocket(uil,c,This,"is_eraser",0); laEndRow(uil,b);
@@ -172,22 +199,28 @@ void ourRegisterNodes(){
     pc=laAddPropertyContainer("our_node_brush_settings", "Brush Settings", "Brush settings node to read from",0,ui_BrushSettingsNode,sizeof(OurBrushSettingsNode),0,0,1);
     OUR_PC_IDN_BRUSH_SETTINGS=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
+    laAddSubGroup(pc,"canvas_scale", "Canvas scale","Canvas scale","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,CanvasScale),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"size", "Size","Size","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Size),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"transparency", "Transparency","Transparency","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Transparency),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"hardness", "Hardness","Hardness","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Hardness),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"smudge", "Smudge","Smudge","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Smudge),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"smudge_length", "Smudge Length","Smudge length","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,SmudgeLength),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"dabs_per_size", "Dabs Per Size","Dabs per size","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,DabsPerSize),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"slender", "Slender","Slender","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Slender),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"angle", "Angle","Angle","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Angle),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 
     pc=laAddPropertyContainer("our_node_brush_outputs", "Brush Outputs", "Brush outputs to draw actual dabs",0,ui_BrushOutputsNode,sizeof(OurBrushOutputsNode),0,0,1);
     OUR_PC_IDN_BRUSH_OUTPUTS=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
+    laAddSubGroup(pc,"position", "Position","Position","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Position),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"size", "Size","Size","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Size),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"transparency", "Transparency","Transparency","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Transparency),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"hardness", "Hardness","Hardness","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Hardness),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"smudge", "Smudge","Smudge","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Smudge),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"smudge_length", "Smudge Length","Smudge length","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,SmudgeLength),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"dabs_per_size", "Dabs Per Size","Dabs per size","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,DabsPerSize),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"slender", "Slender","Slender","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Slender),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"angle", "Angle","Angle","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Angle),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 
     pc=laAddPropertyContainer("our_node_brush_device", "Brush Device", "Brush device input",0,ui_BrushDeviceNode,sizeof(OurBrushDeviceNode),0,0,1);
     OUR_PC_IDN_BRUSH_DEVICE =pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
@@ -195,6 +228,7 @@ void ourRegisterNodes(){
     laAddSubGroup(pc,"pressure","Pressure","Pressure of the input","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,Pressure),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"tilt", "Tilt","Pen tilt vector","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,Tilt),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"position", "Dab position","Interpolated dab position","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,Position),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"last_position", "Last position","Position of the previous dab","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,LastPosition),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"is_eraser", "Is Eraser","Input event is from an eraser","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,IsEraser),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 
     LA_IDN_REGISTER("Brush Settings",L'🖌',OUR_IDN_BRUSH_SETTINGS,OUR_PC_IDN_BRUSH_SETTINGS, IDN_BrushSettingsInit, IDN_BrushSettingsDestroy, IDN_BrushSettingsVisit, IDN_BrushSettingsEval, OurBrushSettingsNode);

+ 40 - 11
ouroperations.c

@@ -15,8 +15,17 @@ uniform vec2 uBrushCenter;\n\
 uniform float uBrushSize;\n\
 uniform float uBrushHardness;\n\
 uniform float uBrushSmudge;\n\
+uniform float uBrushSlender;\n\
+uniform float uBrushAngle;\n\
 uniform vec4 uBrushColor;\n\
 uniform vec4 uBackgroundColor;\n\
+float atan2(in float y, in float x){\n\
+    bool s = (abs(x) > abs(y)); return mix(3.1415926535/2.0 - atan(x,y), atan(y,x), s);\n\
+}\n\
+vec2 rotate(vec2 v, float angle) {\n\
+  float s = sin(angle); float c = cos(angle);\n\
+  return mat2(c,-s,s,c) * v;\n\
+}\n\
 vec4 mix_over(vec4 colora, vec4 colorb){\n\
     vec4 c; c.a=colora.a+colorb.a*(1-colora.a);\n\
     c.rgb=(colora.rgb+colorb.rgb*(1-colora.a));\n\
@@ -35,12 +44,15 @@ subroutine void BrushRoutines();\n\
 subroutine(BrushRoutines) void DoDabs(){\n\
     ivec2 px = ivec2(gl_GlobalInvocationID.xy)+uBrushCorner;\n\
     if(px.x<0||px.y<0||px.x>1024||px.y>1024) return;\n\
-    float dd=distance(vec2(px),uBrushCenter); if(dd>uBrushSize) return;\n\
-    vec4 final;\n\
+    vec2 fpx=vec2(px);\n\
+    fpx=uBrushCenter+rotate(fpx-uBrushCenter,uBrushAngle);\n\
+    fpx.x=uBrushCenter.x+(fpx.x-uBrushCenter.x)*(1+uBrushSlender);\n\
+    float dd=distance(fpx,uBrushCenter); if(dd>uBrushSize) return;\n\
     vec4 dabc=imageLoad(img, px);\n\
     vec4 smudgec=imageLoad(smudge_buckets,ivec2(0,0));\n\
-    dab(dd,uBrushColor,uBrushSize,uBrushHardness,uBrushSmudge,smudgec,dabc,final);\n\
-    dabc=final;\n\
+    vec4 final_color;\n\
+    dab(dd,uBrushColor,uBrushSize,uBrushHardness,uBrushSmudge,smudgec,dabc,final_color);\n\
+    dabc=final_color;\n\
     imageStore(img, px, dabc);\n\
 }\n\
 subroutine(BrushRoutines) void DoSample(){\n\
@@ -167,6 +179,8 @@ void ourui_ToolsPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps
             OUR_BR laShowItem(uil,c,0,"our.tools.current_brush.size")->Expand=1; OUR_PRESSURE("pressure_size") OUR_ER
             OUR_BR laShowItem(uil,c,0,"our.tools.current_brush.transparency")->Expand=1; OUR_PRESSURE("pressure_transparency")  OUR_ER
             OUR_BR laShowItem(uil,c,0,"our.tools.current_brush.hardness")->Expand=1;  OUR_PRESSURE("pressure_hardness") OUR_ER
+            laShowItem(uil,c,0,"our.tools.current_brush.slender");
+            laShowItem(uil,c,0,"our.tools.current_brush.angle");
             OUR_BR laShowItem(uil,c,0,"our.tools.current_brush.smudge")->Expand=1;  OUR_PRESSURE("pressure_smudge")  OUR_ER
             laShowItem(uil,c,0,"our.tools.current_brush.dabs_per_size");
             laShowItem(uil,c,0,"our.tools.current_brush.smudge_resample_length");
@@ -779,21 +793,27 @@ void our_PaintResetBrushState(OurBrush* b){
 real our_PaintGetDabStepDistance(real Size,real DabsPerSize){
     real d=Size/DabsPerSize; if(d<1e-2) d=1e-2; return d;
 }
-int our_PaintGetDabs(OurBrush* b, OurLayer* l, real x, real y, real xto, real yto, real last_pressure, real pressure, int *tl, int *tr, int* tu, int* tb){
+int our_PaintGetDabs(OurBrush* b, OurLayer* l, real x, real y, real xto, real yto,
+    real last_pressure, real last_angle_x, real last_angle_y, real pressure, real angle_x, real angle_y, int *tl, int *tr, int* tu, int* tb){
     Our->NextDab=0;
     if(!b->EvalDabsPerSize) b->EvalDabsPerSize=b->DabsPerSize;
     real size=b->Size; real dd=our_PaintGetDabStepDistance(b->EvalSize, b->EvalDabsPerSize); real len=tnsDistIdv2(x,y,xto,yto); real rem=b->BrushRemainingDist;
     real alllen=len+rem; real uselen=dd,step=0; if(!len)return 0; if(dd>alllen){ b->BrushRemainingDist+=len; return 0; }
     real xmin=FLT_MAX,xmax=-FLT_MAX,ymin=FLT_MAX,ymax=-FLT_MAX;
     b->EvalSize=b->Size; b->EvalHardness=b->Hardness; b->EvalSmudge=b->Smudge; b->EvalSmudgeLength=b->SmudgeResampleLength;
-    b->EvalTransparency=b->Transparency; b->EvalDabsPerSize=b->DabsPerSize;
+    b->EvalTransparency=b->Transparency; b->EvalDabsPerSize=b->DabsPerSize; b->EvalSlender=b->Slender; b->EvalAngle=b->Angle;
+
+    if(Our->ResetBrush){ b->LastX=x; b->LastY=y; /*b->EvalLastAngle=atan2(yto-y,xto-x);*/ Our->ResetBrush=0; }
     while(1){
         arrEnsureLength(&Our->Dabs,Our->NextDab,&Our->MaxDab,sizeof(OurDab)); OurDab* od=&Our->Dabs[Our->NextDab];
         real r=tnsGetRatiod(0,len,uselen-rem); od->X=tnsInterpolate(x,xto,r); od->Y=tnsInterpolate(y,yto,r); TNS_CLAMP(r,0,1);
         if(b->UseNodes){
             b->EvalPressure=tnsInterpolate(last_pressure,pressure,r); b->EvalPosition[0]=od->X; b->EvalPosition[1]=od->Y;
+            b->EvalPositionOut[0]=od->X; b->EvalPositionOut[1]=od->Y;
+            b->EvalTilt[0]=tnsInterpolate(last_angle_x,angle_x,r); b->EvalTilt[1]=tnsInterpolate(last_angle_y,angle_y,r);
             ourEvalBrush();
             TNS_CLAMP(b->EvalSmudge,0,1); TNS_CLAMP(b->EvalSmudgeLength,0,100000); TNS_CLAMP(b->EvalTransparency,0,1); TNS_CLAMP(b->EvalHardness,0,1);  TNS_CLAMP(b->DabsPerSize,0,100000);
+            od->X=b->EvalPositionOut[0]; od->Y=b->EvalPositionOut[1];
         }
         if(!b->EvalDabsPerSize) b->EvalDabsPerSize=1;
 #define pfac(psw) (((!b->UseNodes)&&psw)?tnsInterpolate(last_pressure,pressure,r):1)
@@ -801,6 +821,8 @@ int our_PaintGetDabs(OurBrush* b, OurLayer* l, real x, real y, real xto, real yt
         od->Smudge = b->EvalSmudge*pfac(b->PressureSmudge); od->Color[3]=pow(b->EvalTransparency*pfac(b->PressureTransparency),2.718);
         tnsVectorSet3v(od->Color,Our->CurrentColor);
 #undef pfac;
+        od->Slender = b->EvalSlender; od->Angle=b->EvalAngle;
+        b->LastX=od->X; b->LastY=od->Y;
         xmin=TNS_MIN2(xmin, od->X-od->Size); xmax=TNS_MAX2(xmax, od->X+od->Size); 
         ymin=TNS_MIN2(ymin, od->Y-od->Size); ymax=TNS_MAX2(ymax, od->Y+od->Size);
         if(od->Size>1e-1) Our->NextDab++;
@@ -836,6 +858,8 @@ void our_PaintDoDab(OurDab* d, int tl, int tr, int tu, int tb){
     glUniform1f(Our->uBrushSize,d->Size);
     glUniform1f(Our->uBrushHardness,d->Hardness);
     glUniform1f(Our->uBrushSmudge,d->Smudge);
+    glUniform1f(Our->uBrushSlender,d->Slender);
+    glUniform1f(Our->uBrushAngle,d->Angle);
     glUniform4fv(Our->uBrushColor,1,d->Color);
     glDispatchCompute(ceil(d->Size/16), ceil(d->Size/16), 1);
     glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
@@ -1028,10 +1052,10 @@ int ourinv_MoveBrush(laOperator* a, laEvent* e){
 int ourinv_Action(laOperator* a, laEvent* e){
     OurLayer* l=Our->CurrentLayer; OurCanvasDraw *ex = a->This?a->This->EndInstance:0; OurBrush* ob=Our->CurrentBrush; if(!l||!ex||!ob) return LA_CANCELED;
     our_PaintResetBrushState(ob);
-    real x,y; our_UiToCanvas(&ex->Base,e,&x,&y); ex->CanvasLastX=x;ex->CanvasLastY=y;ex->LastPressure=e->Pressure;
+    real x,y; our_UiToCanvas(&ex->Base,e,&x,&y); ex->CanvasLastX=x;ex->CanvasLastY=y;ex->LastPressure=e->Pressure;ex->LastTilt[0]=e->AngleX;ex->LastTilt[1]=e->AngleY;
     ex->CanvasDownX=x; ex->CanvasDownY=y;
-    Our->ActiveTool=Our->Tool;
-    Our->xmin=FLT_MAX;Our->xmax=-FLT_MAX;Our->ymin=FLT_MAX;Our->ymax=-FLT_MAX;
+    Our->ActiveTool=Our->Tool; Our->CurrentScale = 1.0f/ex->Base.ZoomX;
+    Our->xmin=FLT_MAX;Our->xmax=-FLT_MAX;Our->ymin=FLT_MAX;Our->ymax=-FLT_MAX; Our->ResetBrush=1;
     if(Our->ActiveTool==OUR_TOOL_CROP){ if(!Our->ShowBorder) return LA_FINISHED; our_StartCropping(ex); }
     return LA_RUNNING;
 }
@@ -1045,11 +1069,12 @@ int ourmod_Paint(laOperator* a, laEvent* e){
     if(e->Type==LA_MOUSEMOVE||e->Type==LA_L_MOUSE_DOWN){
         real x,y; our_UiToCanvas(&ex->Base,e,&x,&y);
         int tl,tr,tu,tb;
-        if(our_PaintGetDabs(ob,l,ex->CanvasLastX,ex->CanvasLastY,x,y,ex->LastPressure,e->Pressure,&tl,&tr,&tu,&tb)){
+        if(our_PaintGetDabs(ob,l,ex->CanvasLastX,ex->CanvasLastY,x,y,
+            ex->LastPressure,ex->LastTilt[0],ex->LastTilt[1],e->Pressure,e->AngleX,e->AngleY,&tl,&tr,&tu,&tb)){
             our_PaintDoDabsWithSmudgeSegments(l,tl,tr,tu,tb);
             laNotifyUsers("our.canvas"); laMarkMemChanged(Our->CanvasSaverDummyList.pFirst);
         }
-        ex->CanvasLastX=x;ex->CanvasLastY=y;ex->LastPressure=e->Pressure;
+        ex->CanvasLastX=x;ex->CanvasLastY=y;ex->LastPressure=e->Pressure;ex->LastTilt[0]=e->AngleX;ex->LastTilt[1]=e->AngleY;
     }
 
     return LA_RUNNING;
@@ -1241,6 +1266,8 @@ void ourRegisterEverything(){
     laAddFloatProperty(pc,"smudge","Smudge","Smudge of the brush",0,0,0,1,0,0.05,0.95,0,offsetof(OurBrush,Smudge),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"dabs_per_size","Dabs Per Size","How many dabs per size of the brush",0,0,0,0,0,0,0,0,offsetof(OurBrush,DabsPerSize),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"smudge_resample_length","Smudge Resample Length","How long of a distance (based on size) should elapse before resampling smudge",0,0,0,0,0,0,0,0,offsetof(OurBrush,SmudgeResampleLength),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"slender","Slender","Slenderness of the brush",0,0, 0,10,0,0.1,0,0,offsetof(OurBrush,Slender),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"angle","Angle","Angle of the brush",0,0, 0,TNS_PI,-TNS_PI,0.1,0,0,offsetof(OurBrush,Angle),0,0,0,0,0,0,0,0,0,0,LA_RAD_ANGLE);
     p=laAddEnumProperty(pc,"pressure_size","Pressure Size","Use pen pressure to control size",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(OurBrush,PressureSize),0,0,0,0,0,0,0,0,0,0);
     OUR_ADD_PRESSURE_SWITCH(p);
     p=laAddEnumProperty(pc,"pressure_transparency","Pressure Transparency","Use pen pressure to control transparency",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(OurBrush,PressureTransparency),0,0,0,0,0,0,0,0,0,0);
@@ -1338,6 +1365,8 @@ void ourInit(){
     Our->uBrushHardness=glGetUniformLocation(Our->CanvasProgram,"uBrushHardness");
     Our->uBrushSmudge=glGetUniformLocation(Our->CanvasProgram,"uBrushSmudge");
     Our->uBrushColor=glGetUniformLocation(Our->CanvasProgram,"uBrushColor");
+    Our->uBrushSlender=glGetUniformLocation(Our->CanvasProgram,"uBrushSlender");
+    Our->uBrushAngle=glGetUniformLocation(Our->CanvasProgram,"uBrushAngle");
 
     Our->uBrushRoutineSelection=glGetSubroutineUniformLocation(Our->CanvasProgram, GL_COMPUTE_SHADER, "uBrushRoutineSelection");
     Our->RoutineDoDabs=glGetSubroutineIndex(Our->CanvasProgram, GL_COMPUTE_SHADER, "DoDabs");

+ 1 - 1
ourpaint.c

@@ -14,7 +14,7 @@ int main(int argc, char *argv[]){
 
     //laAddRootDBInst("la.input_mapping");
     //laAddRootDBInst("la.drivers");
-    laAddRootDBInst("our.tools");
+    //laAddRootDBInst("our.tools");
 
     laWindow* w = laDesignWindow(-1,-1,600,600);
 

+ 24 - 3
ourpaint.h

@@ -17,6 +17,7 @@ STRUCTURE(OurCanvasDraw){
     real CanvasLastX,CanvasLastY;
     real CanvasDownX,CanvasDownY;
     real LastPressure;
+    real LastTilt[2];
 };
 
 
@@ -55,21 +56,27 @@ STRUCTURE(OurLayerRead){
 
 STRUCTURE(OurBrushSettingsNode){
     laBaseNode Base;
+    laNodeOutSocket* CanvasScale;  real rCanvasScale;
     laNodeOutSocket* Size;         real rSize;
     laNodeOutSocket* Transparency; real rTransparency;
     laNodeOutSocket* Hardness;     real rHardness;
     laNodeOutSocket* Smudge;       real rSmudge;
-    laNodeOutSocket* DabsPerSize;real rDabsPerSize;
+    laNodeOutSocket* DabsPerSize;  real rDabsPerSize;
     laNodeOutSocket* SmudgeLength; real rSmudgeLength;
+    laNodeOutSocket* Slender;      real rSlender;
+    laNodeOutSocket* Angle;        real rAngle;
 };
 STRUCTURE(OurBrushOutputsNode){
     laBaseNode Base;
+    laNodeInSocket* Position;
     laNodeInSocket* Size;
     laNodeInSocket* Transparency;
     laNodeInSocket* Hardness;
     laNodeInSocket* Smudge;
     laNodeInSocket* DabsPerSize;
     laNodeInSocket* SmudgeLength;
+    laNodeInSocket* Slender;
+    laNodeInSocket* Angle;
 };
 STRUCTURE(OurBrushDeviceNode){
     laBaseNode Base;
@@ -77,6 +84,7 @@ STRUCTURE(OurBrushDeviceNode){
     laNodeOutSocket* Position; real rPosition[2];
     laNodeOutSocket* Tilt;     real rTilt[2];
     laNodeOutSocket* IsEraser; int  rIsEraser;
+    laNodeOutSocket* LastPosition; real rLastPosition[2];
 };
 
 STRUCTURE(OurBrush){
@@ -87,19 +95,25 @@ STRUCTURE(OurBrush){
     real Hardness;
     real Transparency;
     real Smudge;
-    real SmudgeResampleLength; real SmudgeAccum; int SmudgeRestart;
-    real BrushRemainingDist;
+    real SmudgeResampleLength; real SmudgeAccum; int SmudgeRestart; real BrushRemainingDist;
+    real Slender;
+    real Angle;
     int PressureSize,PressureHardness,PressureTransparency,PressureSmudge; // the simple way
 
     int UseNodes; // the flexible way
     laRackPage* Rack;
+
+    real LastX,LastY;
     
+    real EvalPositionOut[2];
     real EvalSize;
     real EvalDabsPerSize;
     real EvalHardness;
     real EvalTransparency;
     real EvalSmudge;
     real EvalSmudgeLength;
+    real EvalSlender;
+    real EvalAngle;
 
     real EvalPressure;
     real EvalPosition[2];
@@ -112,6 +126,8 @@ STRUCTURE(OurDab){
     float Hardness;
     float Smudge; int ResampleSmudge;
     float Color[4];
+    float Slender;
+    float Angle;
 };
 
 STRUCTURE(OurUndoTile){
@@ -141,6 +157,8 @@ STRUCTURE(OurPaint){
     OurDab* Dabs; int NextDab,MaxDab;
     laListHandle BrushEval;
 
+    real CurrentScale;
+
     int Tool,ActiveTool;
     int X,Y,W,H; //border
     int ShowBorder,UseBorder;
@@ -154,6 +172,8 @@ STRUCTURE(OurPaint){
     GLint uBrushHardness;
     GLint uBrushSmudge;
     GLint uBrushColor;
+    GLint uBrushSlender;
+    GLint uBrushAngle;
     GLint uBrushRoutineSelection;
     GLint RoutineDoDabs;
     GLint RoutineDoSample;
@@ -164,6 +184,7 @@ STRUCTURE(OurPaint){
     real BorderAlpha;
 
     real xmin,xmax,ymin,ymax; // stroke bbox for undo region
+    int ResetBrush;
 
     uint16_t *ImageBuffer;
     int ImageW,ImageH,ImageX,ImageY,LoadX,LoadY,TempLoadX,TempLoadY;