PDA

View Full Version : Spirograph



terrygillooly
07-26-2011, 02:39 PM
My fellow ArtRagers, what key feature were you crushed to realize was left out of version 3.5?

Is it vectors?

Perspective options?

A "Bob Ross" slider for the Oil Brush?

Wrong!

It's a spirograph generator! ;) But now you can fill the void. Simply copy and paste the below code into a UTF-16 text file per Andy's instructions (http://www2.ambientdesign.com/forums/showthread.php?t=35944). (edit: or, click on the file and save to your Scripts directory)

The generator works on the current painting with your selected drawing tool (try it with eraser on a colored background). It should work with both 3.5 and 3.5 Pro.

If you run into any problems, please let me know!

edit on 8/3/11: made minor change to multicolor code, hopefully without breaking anything


//================================================== =========================
//================================================== =========================
// ArtRage Script File.
//================================================== =========================
//================================================== =========================


//================================================== =========================
// Version Block - Script version and ArtRage version:
//================================================== =========================

<Version>
ArtRage Version: ArtRage 3 Studio Pro
ArtRage Build: 3.5.1
Professional Edition: No
Script Version: 1
</Version>


//================================================== =========================
// Header block - Info about the painting/person who generated this script:
//================================================== =========================

<Header>
// === Project data
Painting Name: "untitled"
Painting Width: 1280
Painting Height: 1024
Painting DPI: 72
// === Author data
Author Name: "Ti-Chen Feng"
Script Name: "Epicycloid"
Comment: "a 'spirograph' generator"
Script Type: ""
Script Feature Flags: 0
</Header>


//================================================== =========================
// Script data follows:
//================================================== =========================

//function: changes the foreground color (this method of color variation does not work for some tools)
void SetColor( real rAmount ) {
real rHue = ColourH()

rHue+=rAmount
if (rHue>1) rHue--
SetColourHLS( rHue, ColourL(), ColourS() )

return
}


//function: draws the epicycloid
void DrawEpicycloid(int nRes, real R, real r, real d, real rCenterX, real rCenterY, real rReps, flag fColorChange) {

//set up initial values (at theta = 0 rad)
flag fContinue = YES
int i = 0
int nTool = CurrentToolID() //Done in main, but I couldn't take the idea of passing yet another parameter
int nColType = 0 //0=none, 1=normal, 2=paint, 3=ink
real rColAmount = 0.004 //default
real x = rCenterX + R + r + d
real y = rCenterY
real delta = 2*3.14159265/(nRes+1)
real theta = 0
real rPressure = 0.235

//fudging to mimic the additional pressure exerted by user
rPressure += CurrentToolSetting(0x0B2D05E34)
if (rPressure>1) rPressure=1

//Depending on drawing tool, set up multicolor type
if( fColorChange ) {
//default: pencil (precise off), crayon, chalk, tube, glitter (mc off), sticker spray
nColType = 1
//oil, felt pen, knife (flat or edge), roller, wc
if( nTool==4900 || nTool==4904 || (nTool==4905 && CurrentToolSetting(0x0B2D05E60)<2) || nTool==4915 || nTool==4916 ) {
nColType = 2
rColAmount = 0.4
}
//pencil (precise), airbrush, ink, gloop
if( nTool==4901 || nTool==4914 || nTool==4917 || nTool==4918 ) {
nColType = 3
rColAmount = 0.09
}
//knife (other), eraser, glitter (multicolor)
if( (nTool==4905 && CurrentToolSetting(0x0B2D05E60)>1) || nTool==4906 || (nTool==4913 && CurrentToolSetting(0x0B2D05E3E)>0) ) {
nColType = 0
}
}
//MessageBox("color type = %%nColType")

//The while loop lets user continue if the results are interesting
//The stroke event is broken up to allow Undo
while (rReps>0) {
<StrokeEvent>
<StrokeHeader>
<EventPt> Wait: 0.000s Loc: (x, y) Pr: rPressure Ti: 1 Ro: 0 Rv: NO Iv: NO </EventPt>
<Recorded> No </Recorded>
</StrokeHeader>

// MouseDown point
Loc: (x, y) Pr: rPressure Ti: 1 Ro: 0 Rv: NO Iv: NO

//Draw the requested number of iterations
for (i=0; i<=rReps*nRes; i++) {
Loc: (x, y) Pr: rPressure Ti: 1 Ro: 0 Rv: NO Iv: NO
x = rCenterX + (R+r)*cos(theta) + d*cos(((R+r)/r)*theta)
y = rCenterY + (R+r)*sin(theta) + d*sin(((R+r)/r)*theta)
theta+=delta
if( nColType==1 ) SetColor(rColAmount)
}

// MouseUp point.
Loc: (x, y) Pr: rPressure Ti: 1 Ro: 0 Rv: NO Iv: NO
</StrokeEvent>

//Continue or stop?
InputBox("Stationary circle: %%R\nMoving circle: %%r\nDrawing arm: %%d\nResolution: %%nRes\nNumber of cycles to draw (0-100)$$rReps")
if( rReps>100 ) rReps=100
if( nColType>1 ) SetColor(rColAmount)
}
return
}



<Events>

flag fGradient = NO //whether or not to change hue during stroke event
flag fNewLayer = YES //put epicycloid on a new layer or use the current layer?
int nTool = CurrentToolID() //user's selected tool
int nResolution = 180 //how often to calculate (x,y) per cycle
real rCycle = 9.01 //how many cycles to draw per iteration (one cycle being 360 degrees around stator)
real rMaxRadX = PaintingWidth()/2
real rMaxRadY = PaintingHeight()/2
real rMaxRad = rMaxRadX
real rStaRad = 100 //radius of stator
real rRotRad = -45 //radius of rotor; rotor is outside of stator if positive, inside if negative
real rArmLen = -55 //length of a drawing arm fixed at center of rotor; starts pointing out (pos) or in (neg)

//Check to make sure selected tool is not: sampler, xform, text, fill, or select
if( nTool==4909 || nTool==4910 || nTool==4919 || nTool==4921 || nTool==4922 ) {
MessageBox("Please select an appropriate tool!")
exit
}

//Maxiumum allowed radius is half the height or width (whichever is greater) of the canvas
if (rMaxRadY>rMaxRadX) rMaxRad=rMaxRadY

InputBox("Radius of stationary circle$$rStaRad\nRadius of moving circle$$rRotRad\nDrawing arm length$$rArmLen\nResolution (2-360)$$nResolution\nNumber of cycles (1-100)$$rCycle\nMulticolor?$$fGradient\nDraw on new layer?$$fNewLayer")
//Verify input values:
// Radius of stator must be positive. Clamp maximum radius so the epicycloid is visible
if (rStaRad<0) rStaRad=-rStaRad
if (rStaRad>rMaxRad) rStaRad=rMaxRad
// Clamp maximum radius of rotor. For hypotrochoid, clamp radius to rStaRad. 0 is disallowed.
if (rRotRad>rMaxRad) rRotRad=rMaxRad
if (rRotRad<-rStaRad) rRotRad=-rStaRad
if (rRotRad==0) rRotRad=rStaRad/100
// Clamp drawing arm length.
if (rArmLen>rMaxRad) rArmLen=rMaxRad
if (rArmLen<-rMaxRad) rArmLen=-rMaxRad
// nResolution is from 2 (draws 2 points per cycle) to 360 (draws 360 points)
if (nResolution<2) nResolution=2
if (nResolution>360) nResolution=360
// Number of cycles per iteration can be from 0 to, say, 100
if (rCycle<0) rCycle=0
if (rCycle>100) rCycle=100

//Make a new layer, if requested
if (fNewLayer) Wait: 0 EvType: Command CommandID: Add New Layer

//Draw!
DrawEpicycloid(nResolution, rStaRad, rRotRad, rArmLen, rMaxRadX, rMaxRadY, rCycle, fGradient)

//110803

P.S. Here are some spirograph apps on the Internet:
http://www.mathplayground.com/SpirographMath.html
http://www.math.psu.edu/dlittle/java/parametricequations/spirograph/SpiroGraph1.0/index.html
http://perl.guru.org/lynn/apps/

AndyRage
07-26-2011, 02:50 PM
That's seriously very cool! Nicely done. :)

Can I suggest one tiny change though - change the
Professional Edition: Yes
flag to 'NO' instead. Your script doesn't use any features that cant be used in ArtRage Studio (not the Pro edition) so it would save a message box popping up with users who dont have Studio Pro.

terrygillooly
07-26-2011, 03:41 PM
Oh, yes, thanks for the tip! So that's what that flag does.



That's seriously very cool! Nicely done. :)

Also, basking. :cool:

AndyRage
07-26-2011, 04:13 PM
If you want me to complicate your life a bit more...
Some of the tools have a problem with changing colour during a stroke - I see by the comments in your script that you'd noticed. Here's why:

The InkPen, AirBrush, Gloop and Precise Pencil dont paint directly into the paint layer. They paint into a temporary 16-bit greyscale mask (to get better compositing resolution) which is mixed with the current paint colour into the paint map at mouse up. What you're seeing with those tools in your script is each tiny change in the screen area during the stroke taking the greyscale map and mixing it with the current colour at the time, and displaying that on the screen. That's not what actually gets put into the paint map. Then at the end of the stroke the entire temporary map is mixed with the new current colour and composited into your layer paint map. So it seems to suddenly revert to one colour.

If you wanted to make the script deal with that, test if the current tool is inkpen, airbrush, gloop, or pencil with the precise flag set. If it is then instead of doing a continuous stroke to a closely spaced sequence of individual single dabs. Each dab has a mouse up, and every mouse up composits the change into the map in the current colour at the time. It will appear as though you are doing a multicoloured stroke, and it will be correct in the layer map.

AndyRage
07-26-2011, 04:14 PM
Alternatively, switch off the 'multi-colour' option for those tools. :)

terrygillooly
07-26-2011, 05:45 PM
Andy, thank you for explaining about the color issue! I'd been really scratching my head over that.

I notice, also, that changing the color did not work at all for the Oil Brush, Paint Roller, and Palette Knife. Do these tools only read the color value at the beginning of a stroke?


If you wanted to make the script deal with that, test if the current tool is inkpen, airbrush, gloop, or pencil with the precise flag set. If it is then instead of doing a continuous stroke to a closely spaced sequence of individual single dabs. Each dab has a mouse up, and every mouse up composits the change into the map in the current colour at the time. It will appear as though you are doing a multicoloured stroke, and it will be correct in the layer map.

Hm. Does that mean a separate stroke event for each color change?

Or can a single stroke event have multiple mouse ups/downs? What would that code look like?

AndyRage
07-27-2011, 08:16 AM
Andy, thank you for explaining about the color issue! I'd been really scratching my head over that.

I notice, also, that changing the color did not work at all for the Oil Brush, Paint Roller, and Palette Knife. Do these tools only read the color value at the beginning of a stroke?


The Oil Brush and the like work kinda like the real thing, I'm afraid. At the start of a stroke the brush head is loaded with a volume of paint of the current colour and that volume of paint 'runs out' as the stroke progresses. If the stroke intersects paint already on the canvas it picks up that paint. So during the stroke the current colour is (almost) completely ignored.




Hm. Does that mean a separate stroke event for each color change?

Or can a single stroke event have multiple mouse ups/downs? What would that code look like?

You'd so something like:


<StrokeEvent>
<StrokeHeader>
<EventPt> Loc: (rX,rY) Pr: 1 Ti: 1 Ro: 0 Rv: NO Iv: NO </EventPt>
<Recorded> No </Recorded>
</StrokeHeader>
Loc: (rX,rY) Pr: 1.0 Ti: 1 Ro: 0 Rv: NO Iv: NO
</StrokeEvent>


That should give you a single splat of paint at the rX, rY coordinate.

terrygillooly
07-27-2011, 07:39 PM
Hm. Unfortunately, dabbing instead of a making continuous stroke left me with dots instead of curves at the lower resolutions. I think I'll leave it the way it is for now.

Juz
07-28-2011, 05:55 AM
Haha Terry, loved your script 'plug' intro... was almost expecting 'But wait... there's more' in there :D:)

This sounds like an interesting one, can't wait to try it out... thanks for your efforts :)

CarlosArt
09-10-2011, 10:55 PM
Thanks,

Fun Script

Carlos