| PureBrush
This is the long-hair brush in Pure ink. You can adjust its size, brush load, pigment wetness and color for different visual effects. You can set its position, tilt, rotation angle and others during brushing.
|
Syntax
PureBrush(float size,
float brushLoad,
float w,
color c)
PureBrush(float size,
float brushLoad,
float w1,
color c1,
float w2,
color c2)
Create an instance of PureBrush with specific size, brushLoad, pigment wetness and color. You can create a PureBrush to carry single pigment or dual pigments. In single pigment, a pigment is indicated by w and c which represent wetness and color respectively. In dual pigment, pigment 1 (indicated by w1 and c1) is on the brush tip and pigment 2 (indicated by w2 and c2) is on the brush tuft. Using the 4-argument constructor for the case of single pigment and 6-argument constructor for the case of dual pigment. The following example creates a PureBrush with dual pigments.
Example
PureBrush myBrush;
void setup()
{
float size = 0.3;
float brushLoad = 1;
float w1 = 0.1;
color c1 = color(0, 0, 0);
float w2 = 0.3;
color c2 = color(255, 255, 255);
myBrush = new PureBrush(size, brushLoad, w1, c1, w2, c2);
}
Parameters
| size | float | The size of the PureBrush Range:
|
||||||
| brushLoad | float | The brush load of the pigment in the PureBrush. The smaller the value, the less the pigment remains in the brush and the stroke usually is dried and discontinuous. Oppositely, the stroke is rich or sharp. Range:
|
||||||
| w | float | The wetness of the pigment taken by the PureBursh. The less the value, the more concentrated is the pigment in the brush and the stroke is usually heavy and more opacity. Oppositely, the stroke is very wet, diluted. Range:
|
||||||
| c | color | The color value of the pigment taken by the PureBrush, in color type. The format and range of the color data can be further set by colorMode(). By default, the color format is in RGB and ranged from 0 to 255. |
Syntax
void dispose()
To delete an instance of PureBrush. This releases memory and reduce program load
Example
PureBrush myBrush;
float x;
float y;
void setup()
{
myBrush = new PureBrush(0.5, 1, 0.1, color(200, 0, 0));
x = 0;
y = height*0.5;
}
void draw()
{
if (x>=0 && x<(width/2))
{
myBrush.write(x, y);
x = x + 3;
}
else if (x>=(width/2))
{
myBrush.dispose(); // the brush is disposed here
}
}
the brush is disposed and the stroke terminates at the middle of the paper
Return
voidSyntax
void lift()
Lifting off the PureBrush from the paper while brushing and this creates a sharp tail to the stroke. After executing this function, let the program run for a while to show the effect. The effect is more obvious if PureBrush moves slow. The following example shows the difference between a brush with lift-off effect and a brush without this effect.
Both lift() and stopWrite() terminate a stroke but they perform differently. lift() takes time to lift off the brush while stopWrite() stops brush writing immediately.
Example
PureBrush myBrush1; // top row, will be lifted
PureBrush myBrush2; // bottom row, will stop without lift off
float x;
float y1, y2;
int phase;
void setup()
{
myBrush1 = new PureBrush(0.3, 1, 0.01, color(0, 0, 0));
myBrush2 = new PureBrush(0.3, 1, 0.01, color(0, 0, 0));
x = width * 0.1;
y1 = height * 0.3;
y2 = height * 0.6;
phase = 1;
}
void draw()
{
if (phase == 1)
{
myBrush1.write(x, y1); // myBrush1 draws stroke on top
myBrush2.write(x, y2); // myBrush2 draws stroke at the bottom
x = x + 1;
if (x >= width * 0.6)
{
myBrush1.lift(); // myBrush1 starts to lift off
phase = 2;
}
}
else if (phase == 2)
{
// myBrush1 need time to lift off here
}
}
the upper stroke drawn by myBrush1 has lift off effect
Return
voidSyntax
void setBrushLoad(float brushLoad)
This function set the amount of pigment carried by the PureBrush. Brush Load affects the continuity of strokes. A vertical brush with low brush load and low pigment wetness is usually hard to deliver pigment to the paper than an inclined brush with similar brush load and wetness.
Example
int numBrush = 10;
int[] x = new int[numBrush];
int y = 10;
float[] brushLoad = new float[numBrush];
float wetness = 0.6;
PureBrush[] myBrush = new PureBrush[numBrush];
void setup()
{
background(255,0);
// set the first brush
x[0] = 50;
brushLoad[0] = 0;
myBrush[0] = new PureBrush(0.3, brushLoad[0], wetness, color(0));
// set the rest of the brushes
for (int i=1; i<numBrush; i++)
{
x[i] = x[i-1]+60;
brushLoad[i] = brushLoad[i-1]+0.1;
myBrush[i] = new PureBrush(0.3, brushLoad[i], wetness, color(0));
}
}
void draw()
{
for (int i=0; i<numBrush; i++)
myBrush[i].write(x[i], y);
y = y + 3; // draw all strokes vertically
}
ten brushes with increasing brushLoad painted one after the other
Parameters
| brushLoad | float | The brush load of the pigment in the PureBrush. The smaller the value, the less the pigment remains in the brush and the stroke usually is dried and discontinuous. Oppositely, the stroke is rich or sharp. A vertical brush with both brushLoad and wetness low is hard to deliver pigment. On the other hand, an inclined brush with both brushLoad and wetness low can still produce discontinuing strokes. Range:
|
Return
voidSyntax
void setColor(color pigmentColor)
void setColor(color pigmentColor1,
color pigmentColor2)
To set the color carried by the PureBrush. You can either set single color or dual colors to the brush. In the case of dual colors, pigmentColor1 will be the color of the brush tip and pigmentColor2 will be the color of the brush tuft. Using the 1-argument function for single pigment case and 2-argument function for dual pigment case.
Example
float x, xStep;
float y, yStep;
float hue;
PureBrush myBrush;
void setup()
{
x = random(0, width);
y = random(0, height);
xStep = round(random(1,10));
yStep = round(random(1,10));
colorMode(HSB, 360, 1, 1);
hue = random(0,360);
myBrush = new PureBrush(0.3, 1, 0.2, color(hue, 1, 1));
}
void draw()
{
myBrush.write(x, y);
x = x + xStep;
y = y + yStep;
collisionCheck();
}
void collisionCheck() // check if the brush hits the edges
{
boolean hit = false;
if (x<0) {
xStep = round(random(1,10)); hit=true; }
if (x>width) {
xStep = -round(random(1,10)); hit=true; }
if (y<0) {
yStep = round(random(1,10)); hit=true; }
if (y>height) {
yStep = -round(random(1,10)); hit=true; }
if (hit) {
hue = (hue+6) % 360;
myBrush.setColor(color(hue, 1, 1)); // change color here
}
}
the brush changes color every time it bounces back from the edge
Parameters
| pigmentColor | color | The color value of the pigment taken by the PureBrush, in color type. The format and range of the color data can be further set by colorMode(). By default, the color format is in RGB and ranged from 0 to 255. |
Return
voidSyntax
void setRotation(float angle)
To set the azimuth angle of the PureBrush. Azimuth angle is the horizontal rotation angle of the brush due to the wrist rotation. The angle ranged from 0 to 360. For 0 degree, the PureBrush inclines with its tip pointing to the north (pointing upward to the screen). When the azimuth angle increases, the wrist rotates clockwise and the brush tip also rotates clockwise.
We suggest you to run setTilt() first before setRotation() so that PureBrush is set inclined with specific angle for horizontal rotation. If setRotation() is run without running setTilt(), PureBrush will be forces to incline with 30 degree until it is set to vertical again by running setTilt(0) later.
Example
float cx, cy;
float r; // the offset between the paper center and the brush
float rot;
PureBrush myBrush;
void setup()
{
cx = width / 2;
cy = height / 2;
r = 100;
rot = 0;
background(255, 0);
myBrush = new PureBrush(0.3, 1, 0.1, color(200, 150, 0), 0.6, color(255));
myBrush.setTilt(30); // before rotate, incline it first
myBrush.setRotation(rot);
}
void draw()
{
float x = cx + r * sin(radians(rot));
float y = cy - r * cos(radians(rot));
myBrush.write(x, y);
rot = rot + 1;
myBrush.setRotation(rot); // to rotate the brush horizontally
if (rot>=360) noLoop();
}
to inclined the brush first and then rotate it horizontally
Parameters
| angle | float |
the azimuth angle that an inclined PureBrush made due to wrist rotation. Range:
|
Return
voidSyntax
void setTilt(float angle)
To set the inclined angle that the Purebrush made with its vertical axis. The larger the angle, the more inclined is the brush. The tilt angle is restricted between 0 and 66. Any angle larger than 66 will be ignored and bounded by 66. setRotation() is suggested to execute one time to set the azimuth angle when the PureBrush changes from vertical to inclined (i.e. when the tilt angle just increases from 0).
The PureBrush in the following example starts as an inclined brush. It inclines less and less at the beginning and remains vertical for a while. It then inclines more before finally returning to a vertical orientation.
Example
float x, y;
float tilt;
PureBrush myBrush;
boolean setRotationOneTime;
void setup()
{
x = 0;
y = height/2;
tilt = 66;
myBrush = new PureBrush(0.2, 1, 0.2, color(0));
myBrush.setTilt(tilt);
myBrush.setRotation(315);
setRotationOneTime = false;
}
void draw()
{
myBrush.write(x, y);
x = x + 2;
// in the first and last thirds of the paper
if (x<width/3 || x>(width*0.667))
tilt = tilt - 1;
// in the middle third of the paper
else
{
tilt = tilt + 1;
if (!setRotationOneTime) // only do setRotation one time here
{
myBrush.setTilt(tilt);
myBrush.setRotation(315);
setRotationOneTime = true;
}
}
tilt = constrain(tilt, 0, 66);
myBrush.setTilt(tilt);
}
the tilt angle decreases in the first and last thirds of the paper, and increases in the middle third.
Parameters
| angle | float |
the angle that the PureBrush made with its vertical axis. Range:
|
Return
voidSyntax
void setWetness(float pigmentWetness)
void setWetness(float pigmentWetness1,
float pigmentWetness2)
To set the wetness for the pigment carried by the PureBrush. PureBrush can be in single color mode or dual color mode. Using the 1-argument function to set the wetness for the single pigment in the brush, and use 2-argument function to set the wetness for the pigment on the brush tip (pigmentWetness1) and the one on the brush tuft (pigmentWetness2).
Always pairing up the use of setColor() and setWetness() for the consistency of color mode in PureBrush. If you use setColor() with 1-argument, then use setWetness() with 1-argument. If you use setColor() with 2-argument, then use setWetness() with 2-argument.
Example
PureBrush brush;
float nx = 0;
float ny = 1000;
float t = 0;
float WETNESS_LOW = 0.01;
float WETNESS_HIGH = 0.9;
void setup()
{
color c = color(random(180), random(180), random(180));
brush = new PureBrush(0.1, 1, WETNESS_LOW, c);
}
void draw()
{
// wetness wave: -cos() starts LOW, rises to HIGH, sinks back, rises again
float wetness = map(-cos(t), -1, 1, WETNESS_LOW, WETNESS_HIGH);
brush.setWetness(wetness);
t += 0.02;
// brush moves in a smooth curved path
float x = map(noise(nx), 0.2, 0.8, 100, width - 100);
float y = map(noise(ny), 0.2, 0.8, 0, height-1);
nx += 0.006;
ny += 0.006;
brush.write(x, y);
}
myBrush moves and keeps changing the wetness of its pigment
Parameters
| pigmentWetness | float | The wetness of the pigment taken by the PureBursh. The less the value, the more concentrated is the pigment in the brush and the stroke is usually heavy and more opacity. Oppositely, the stroke is very wet, diluted. Range:
|
Return
voidSyntax
void stopWrite()
Lifting off the PureBrush from the paper immediately and stop writing. The next execution of PureBrush.write() starts a new stroke.
Beware of the difference between lift() and stopWrite(). lift() takes time to lift off the brush which creates sharp tail while stopWrite() stops brush writing immediately. Applying lift() requires some specific design in the algorithm to allow time for lifting off the brush. On the other hand, the execution of stopWrite() is instant and easier.
Example
PureBrush myBrush1; // left column, stroke is continuous
PureBrush myBrush2; // right column, stroke is separated
float x;
float y;
void setup()
{
myBrush1 = new PureBrush(0.1, 1, 0, color(0));
myBrush2 = new PureBrush(0.1, 1, 0, color(0));
x = width*0.1;
y = height*0.1;
}
void draw()
{
myBrush1.write(x, y);
myBrush2.write(x+(width/2), y);
x = x + 3;
if (x > width * 0.45)
{
myBrush2.stopWrite(); // terminate the old stroke
x = width*0.1;
y = y + height*0.2;
}
}
myBrush1 never lifts off from the paper and keeps drawing the same stroke. myBrush2 lifts off immediately after each horizontal stroke and starts a new one in the next row.
Return
voidSyntax
void write(float x, float y)
Press a PureBrush on the position (x,y) of the paper on the Kinetic Force layer and deliver pigment on it
Example
PureBrush myBrush;
float x;
float y;
void setup()
{
float size = 0.5;
float brushLoad = 1;
float wetness = 0.5;
color pigmentColor = color(200, 0, 0);
myBrush = new PureBrush(size, brushLoad, wetness, pigmentColor);
x=0;
y=0;
}
void draw()
{
myBrush.write(x, y);
x = x + 3;
y = y + 1;
}
myBrush moves and paints along an inclined path
Parameters
| x | float | the x position where the PureBrush is pressed on |
| y | float | the y position where the PureBrush is pressed on |
Return
void
| PurePaper PurePaper let you control certain behaviours of the Yunlong paper for freehand brushwork mode in the Kinetic Force layer
|
Syntax
PurePaper()
To get the instance of paper on the Kinetic Force layer where the PureBrush is working on. Some characteristics of the paper can then be adjusted via this instance. By default, PurePaper is a piece of YunLong paper in freehand brushwork mode
Example
PureBrush myBrush;
PurePaper myPaper;
void setup()
{
myBrush = new PureBrush(0.3, 1, 0.01, color(0, 0, 0));
myPaper = new PurePaper();
}
Syntax
void clear(float strength)
To clear all pigment on paper with a speed controlled by ‘strength’. The following example changes the clearing effect by pressing the mouse key. Pigment sometimes accumulates on PurePaper and sometimes erases quickly.
Example
PureBrush myBrush;
float x;
float y;
PurePaper myPaper;
String mode;
void setup()
{
myBrush = new PureBrush(0.3, 1, 0.6, color(0, 0, 0));
x = random(0, width);
y = random(0, height);
myPaper = new PurePaper();
myPaper.clear(0.5);
mode = "clear";
}
void draw()
{
myBrush.write(x, y);
float shiftX = 50*(random(0,1)-random(0,1)); // move the brush
float shiftY = 50*(random(0,1)-random(0,1));
x = x + shiftX;
y = y + shiftY;
x = constrain(x, 0, width-1);
y = constrain(y, 0, height-1);
}
void mousePressed()
{
if (mode.equals("clear")) // disable the clearing effect
{
myPaper.clear(0);
mode = "accumulate";
}
else if (mode.equals("accumulate")) // enable strong clearing effect
{
myPaper.clear(0.5);
mode = "clear";
}
}
pigment occasionally accumulates and vanishes when a mouse click happens
Parameters
| strength | float | The speed to clear pigment on PurePaper Range:
|
Return
voidSyntax
PImage clone()
This function duplicates the content painted on the root paper layer and return it as a PImage
Example 1
PurePaper myPaper;
PImage img;
void setup()
{
background(255);
myPaper = new PurePaper();
img = myPaper.clone();
myPaper.transfer(img, width/2, 0, 0);
}
this example clones the artwork on the paper layer, saves it as an PImage and displays it on the right hand side of the Kinetic Force layer.
Example 2
PurePaper myPaper;
PImage img;
int dotSize; // the size of a color dot
int gap; // the distance between two dot centers
void setup()
{
// set background to be non-transparent, to hide the artwork on the paper
background(255,255);
myPaper = new PurePaper();
img = myPaper.clone();
dotSize = 6;
gap = 6;
// to sample color and draw the dots
noStroke();
img.loadPixels();
for (int j=0; j<img.height; j=j+gap)
for (int i=0; i<img.width; i=i+gap)
{
int loc = j*img.width + i;
color c = img.pixels[loc];
fill(c);
ellipse(i,j,dotSize, dotSize);
}
}
based on example 1, this example further samples color from the cloned artwork and draw color dots on the Kinetic Force layer. Please beware that the background of Kinetic Force layer is set as non-transparent by 'background(255, 255)' so that the artwork on paper is hided.
Return
PImageSyntax
void dispose()
To delete an instance of PurePaper. This releases memory and reduce program load. In the following example, PurePaper is initialized with strong clearing effect. The pigment stops being cleared when the brush paints over half of the paper since PurePaper is disposed at that time.
Example
PureBrush myBrush;
PurePaper myPaper;
float x;
float y;
void setup()
{
myBrush = new PureBrush(0.2, 1, 0.6, color(0, 0, 0));
x = 0;
y = height/2;
myPaper = new PurePaper();
myPaper.clear(0.6);
}
void draw()
{
myBrush.write(x, y);
x = x + 2;
// PurePaper is disposed here
if (x > width/2)
myPaper.dispose();
}
the clearing effect on pigment disappears when myBrush moves to the paper center. This is because myPaper is disposed at this moment
Return
voidSyntax
void quickDry(float dryness)
To turn PurePaper dry by reducing water of each dot on the paper. After applying quickDry(), the pigment delivered on PurePaper tends to be sharp, clear, and less diffusive. dryness is the percentage of water being reduced for each dot on paper, rather than the dryness level of the paper. Setting dryness as 0.5 does not necessarily means that the paper is semi-dried. If a dot on paper carries high water value, reducing its water by half may still make new pigment diffusive on it. Set 'dryness' higher if you want the paper obviously dry. Set it to 1 if you want the new pigment afterward to be sharp and clear.
Running quickDry() consumes time. You can run it in setup() or in draw(). If you run it in draw(), do not run it iteratively. Adjust your algorithm so that it is executed one time within a certain period of time to avoid overhead.
Example
PureBrush barBrush = new PureBrush(0.4, 1, 0.9, color(200)); // very wet
PureBrush movingBrush = new PureBrush(0.2, 1, 0, color(0));
PurePaper paper = new PurePaper();
int x = 350;
int y = 150;
int xStep = 0;
int yStep = 0;
void setup()
{
for (int x=0; x<width; x+=3) // draw the first bar
barBrush.write(x, 30);
paper.quickDry(1); // make the paper full dry
for (int x=0; x<width; x+=3) // draw the second bar
barBrush.write(x, 80);
paper.quickDry(0.8); // make the paper semi-dry, the second bar becomes semi-dry
for (int x=0; x<width; x+=3) // draw the third bar, no more quickDry()t
barBrush.write(x, 130); // the third bar remain wet
for (int y=0; y<height; y+=3)
movingBrush.write(350,y);
}
void draw() // randomly draw the movingBrush
{
movingBrush.write(x,y);
xStep += random(-3,3);
yStep += random(-3,3);
if(x<0 || x>=width) xStep*=-1;
if(y<0 || y>=height) yStep*=-1;
x += xStep;
y += yStep;
}
quickDry is implemented twice in between the drawing of the three bars, turning the first one to full dry, the second one to semi dry, and keeping the last one wet. Different diffusive effects are created for comparing when another brush with concentrated pigment moves over them.
Return
voidSyntax
void transfer(PImage img, float x, float y, float wetness)
This function can transfer an image in PImage type onto a PurePaper with certain diffusing effect controlled by wetness.This gives painting style to an image and let it further integrate with the ink strokes created by PureBrush. Using loadImage() method from the PImage class in Processing to load your image first. Make sure that the image is located in a 'data' subfolder inside the folder where your Pure ink source file (.pure file) located. transfer() also support transparent image.
Please note that image transfer happens on Kinetic Force layer and does not react with the digital painting on the root paper layer unless you manually tranfer Kinetic Force layer onto the paper layer in Pure ink software.
Example
PurePaper myPaper;
PImage img;
PGraphics pg;
int cellWidth=6;
int cellHeight=3;
int gapSize=5;
void setup()
{
myPaper = new PurePaper();
img = loadImage("img1.jpg");
pg = createGraphics(img.width, img.height);
img.loadPixels();
pg.beginDraw();
pg.background(255,0);
pg.noStroke();
for (int j=0; j<img.height; j=j+cellHeight+gapSize)
for (int i=0; i<img.width; i=i+cellWidth+gapSize)
{
int loc = i + img.width*j;
color c = img.pixels[loc];
pg.fill(c);
pg.rect(i, j, cellWidth, cellHeight);
}
pg.endDraw();
image(img,30,0);
myPaper.transfer(pg.get(), 360, 0, 0.2);
}
a pointillist graphic is prepared based on an image and then transferred onto PurePaper with diffusive effect.
Parameters
| img | PImage | The image to be transferred onto PurePaper | ||||||
| x | float | The x position of the upper left corner of img | ||||||
| y | float | The y position of the upper left corner of img | ||||||
| wetness | float | The wetness applied on the img during transferring, for diffusing effect Range:
|
Return
voidSyntax
void transferMode(int mode)
This function sets the transfer mode for transfer(). There are either OVERLAP mode or COLORMIXING mode for image transfer. In OVERLAP mode, image is transferred on PurePaper by overlapping and hiding the graphics beneath it. In COLORMIXING mode, image is transferred on PurePaper by mixing its color with the color of graphics beneath it. Run transferMode() before transfer(). If transferMode() is missing, the transfer mode by default is OVERLAP. To change the transfer mode, simply running transferMode() again with the new mode.
Example
PImage img;
void setup()
{
PurePaper myPaper;
img = loadImage("flower-mini.jpg");
myPaper = new PurePaper();
myPaper.transferMode(OVERLAP);
for (int i=0; i<10; i++)
myPaper.transfer(img,
random(width*0.1, width*0.3),
random(0, height*0.5), 0.2);
myPaper.transferMode(COLORMIXING);
for (int i=0; i<10; i++)
myPaper.transfer(img,
random(width*0.6, width*0.8),
random(0, height*0.5), 0.2);
}
for comparing, the transferred images on the left hand side overlap with each others while the ones on the right mix together.
Parameters
| mode | int | Either OVERLAP or COLORMIXING |
Return
void
Public Resources We provide additional resources related to this API for developers as reference.
API prompt: This is the prompt that Prue ink created for user request in the software. Replace the phase '#*(user request is here)*#' by your request.
The code to paint a bird (by GPT6): This is the Processing code generated by GTP-6-Astra for painting a bird in ink style by using the API prompt provided above. The request inserted in the API prompt is "Using brush to paint a bird". |