﻿///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Here are a few parameters that you can change to modify the behavior of the patterm
// Feel free to modify the values, don't change the variable names

modelParameters = {
    // scaling of the input pattern
    patternScale : 1,
    
    // This variable defines the type of symmetry.:
    //   Try   6, 7(*),16, 19, 21(*), 23, 26(*),  27, 29, 31(*), 32(**)
    symmetryType : 32, //  use an integer value between 0 and 32. The default is 32.
    // Here is the list of supported symmetries
    //     0: line reflection
    //     1: point reflection, frameZero
    //     2: rotation symmetry with 4 elements around the center
    //     3: translation symmetry
    //     4: glide reflection

    //     5: dilatation symmetry
    //     6: dilative rotation symmetry
    //     7: a different dilative rotation  symmetry
    //     8: infinite dilative rotation symmetry

    // The Frieze tilings (1D tilings on an infinite line
    //     9:  translation frieze
    //   10: glide reflection frieze
    //   11: translation line reflection frieze
    //   12: translation mirror reflection frieze
    //   13: translation point reflection frieze
    //   14: translation double reflection frieze
    //   15: glide reflection rotation frieze

    // The Wallpaper tilings 2D tilings on an infinite plane
    //   16: wallpaperP1 symmetry
    //   17: wallpaperP2 symmetry
    //   18: wallpaperPM symmetry
    //   19: wallpaperPG symmetry
    //   20: wallpaperPMM symmetry
    //   21: wallpaperCM symmetry
    //   22: wallpaperPMG symmetry
    //   23: wallpaperPGG symmetry
    //   24: wallpaperCMM symmetry
    //   25: wallpaperP4 symmetry
    //   26: wallpaperP4M symmetry
    //   27: wallpaperP4G symmetry
    //  28: wallpaperP3 symmetry
    //  29: wallpaperP3M1 symmetry
    //  30: wallpaperP31M symmetry
    //  31: wallpaperP6 symmetry
    //  32: wallpaperP6M symmetry

    // Pattern translation (in percantage of width and height)
    // By modifying this value you will change the layout of the pattern.
    // Try the following pairs of values: 0, 0 or 0, 75 or 75, 0
    // Note that the behavior is different when your pattern is thin in x or y .
    patternTranslationX : 75,   // The default is  75
    patternTranslationY : 75,   // The default is  75

    // Variation of color of the pattern. 
    // For example, value of 0.2 means that each of the red, green, and blue color components
    // will be multiplied by a DIFFERENT random value from interval 0.8 and 1.2. 
    // Set to 0 if you do not want to modify the pattern color.
    colorRandomness : 0.05,    // use a value between 0 and 1. The default is 0.05.

    // Variation of pattern brightness. 
    // For example, value of 0.6 means that each of the red, green, and blue color components
    // will be multiplied by THE SAME random value from interval 0.4 and 1.6. 
    // Set to 0 if you do not want to modify the pattern brightness.
    brightnessRandomness : 0.1   // use a value between 0 and 1. The default is 0.1.
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// You can modify the code below but keep in mind that as with any scripting
// you can break things. Keep a backup copy.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


var pattern = RenderAPI.getParameter(kpsPattern)


function PatternModule(parameters, scale, patternSize) 
{
    this.parameters = parameters
    this.scale = scale
    this.patternSize = patternSize
}


PatternModule.prototype.render = function (api) 
{
        api.pushMatrix()
        // Set the random seed based on the current position - this assures that the color will be modified
        // in the same way for the pattern in the neighboring selected area
        var currentFrame = api.getFrame()
        var seed = (Math.floor(currentFrame.position().x * 2531011)  + Math.floor(currentFrame.position().y * 2531011)) % 2147483647
        if (this.parameters.symmetryType >= 0 && this.parameters.symmetryType <= 8)
            seed = Math.random()*2531011
       //Engine.message ("rendering at position " + currentFrame.position().x + ", " + currentFrame.position().y)
 
        var rc = this.parameters.colorRandomness  // color randomness
        var br = 1 - this.parameters.brightnessRandomness + Engine.rand(seed) * this.parameters.brightnessRandomness*2  // brightness
        api.Color (kFillColor, br *(1 - rc + Engine.rand()*rc*2), br*(1 - rc + Engine.rand()*rc*2), br*(1 - rc + Engine.rand()*rc*2))

        api.translateRel (this.patternSize.x * this.parameters.patternTranslationX * 0.01, this.patternSize.y * this.parameters.patternTranslationY * 0.01) 
        api.scale(this.scale)
        if (skipRotation)
        {
            var angle = 180 / Math.PI * Math.atan2 (currentFrame.heading().x, currentFrame.heading().y)  // get current rotation
            api.rotate (-angle)    // negate the rotation
        }
        //api.scale(this.parameters.patternScale)
        pattern.render(api)
        api.popMatrix()
 }




var frameZero = new Frame2();
var frameOrigin = new Frame2();
frameOrigin.setPosition(0, 0);
frameOrigin.rotateDeg(0.0);

//symmetry.frame = frameOrigin;  // no need to set the frame if it is a default frame at position (0,0) and no rotation

function setSymmetry(symmetry, parameters, patternSize)
{

	switch (parameters.symmetryType)
	{
	case 0:
		symmetry.set(kSymmetryLineReflection, frameZero, kTransformToLocal);
		break;
	case 1:
		symmetry.set(kSymmetryPointReflection, frameZero, kTransformToLocal);
		break;
	case 2:
         // Type, number of elements around the center, frame specifying local position of an element, optional center of rotation (must be a point)
		symmetry.set(kSymmetryRotation, 4, frameZero);
		break;
	case 3:
		symmetry.set(kSymmetryTranslation, 4, 1.0, frameZero, kTransformToLocal);
		break;
	case 4:
		symmetry.set(kSymmetryGlideReflection, 5, 4.0, frameZero, kTransformToLocal);
		break;


		// Number, initial angle, angle increment, initial scale, scale ratio
	case 5:
		symmetry.set(kSymmetryDilatation, 600, 1.0, 1.0 / 1.01, frameZero, kTransformToLocal); 
		break;
	case 6:
		symmetry.set(kSymmetryDilativeRotation, 600, 0.0, kFibonacciAngleInDegrees, 1.0, 1.0 / 1.01, frameZero, kTransformToLocal); 
		break;
	case 7:
		symmetry.set(kSymmetryDilativeRotation, 200, 0.0, kFibonacciAngleInDegrees, 0.0333, 1.02, frameZero, kTransformToLocal); 
		break;
	case 8:
		symmetry.set(kSymmetryInfiniteDilativeRotation, 100, 0.0, kFibonacciAngleInDegrees, 0.5, 1.01, frameZero, kTransformToLocal); 

	}

	var uStep = patternSize.y * 1.25;
	var vStep = patternSize.x * 1.25;

	// The Frieze tilings (1D tilings on an infinite line
	switch (parameters.symmetryType)
	{
	case 9:
		symmetry.set(kSymmetryFriezeTranslation, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 10:
		symmetry.set(kSymmetryFriezeGlideReflection, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 11:
		symmetry.set(kSymmetryFriezeTranslationLineReflection, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 12:
		symmetry.set(kSymmetryFriezeTranslationMirrorReflection, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 13:
		symmetry.set(kSymmetryFriezeTranslationPointReflection, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 14:
		symmetry.set(kSymmetryFriezeTranslationDoubleReflection, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 15:
		symmetry.set(kSymmetryFriezeGlideReflectionRotation, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	}

	uStep = uStep //* 2.5;
	vStep = vStep //* 2.5

	// The Wallpaper tilings 2D tilings on an infinite plane
	switch (parameters.symmetryType)
	{
	case 16:
		symmetry.set(kSymmetryWallpaperP1, uStep, vStep, frameZero, kTransformToLocal);
		break;
	case 17:
		symmetry.set(kSymmetryWallpaperP2, uStep, vStep, frameZero, kTransformToLocal);
		break;
	case 18:
		symmetry.set(kSymmetryWallpaperPM, uStep, vStep, frameZero, kTransformToLocal);
		break;
	case 19:
		symmetry.set(kSymmetryWallpaperPG, uStep, vStep, frameZero, kTransformToLocal);
		break;
	}

	uStep = uStep * 1.5;
	vStep = vStep * 1.5;

	switch (parameters.symmetryType)
	{
	case 20:
		symmetry.set(kSymmetryWallpaperPMM, uStep, vStep, frameZero, kTransformToLocal);
		break;
	case 21:
		symmetry.set(kSymmetryWallpaperCM, uStep, vStep, frameZero, kTransformToLocal);
		break;
	}

	uStep = uStep * 0.75;
	vStep = vStep * 0.75;
	switch (parameters.symmetryType)
	{
	case 22:
		symmetry.set(kSymmetryWallpaperPMG, uStep, vStep, frameZero, kTransformToLocal);
		break;
	case 23:
		symmetry.set(kSymmetryWallpaperPGG, uStep, vStep, frameZero, kTransformToLocal);
		break;
	case 24:
		symmetry.set(kSymmetryWallpaperCMM, uStep, vStep, frameZero, kTransformToLocal);
		break;
	case 25:
		symmetry.set(kSymmetryWallpaperP4, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 26:
		symmetry.set(kSymmetryWallpaperP4M, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 27:
		symmetry.set(kSymmetryWallpaperP4G, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	}

	uStep = uStep * 1.75;
	vStep = vStep * 1.75;
	switch (parameters.symmetryType)
	{
	case 28:
		symmetry.set(kSymmetryWallpaperP3, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 29:
		symmetry.set(kSymmetryWallpaperP3M1, Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 30:
		symmetry.set(kSymmetryWallpaperP31M, Math.max(uStep, vStep), frameZero, kTransformToLocal); 
		break;
	case 31:
		symmetry.set(kSymmetryWallpaperP6,  Math.max(uStep, vStep), frameZero, kTransformToLocal);
		break;
	case 32:
		symmetry.set(kSymmetryWallpaperP6M, Math.max(uStep, vStep), frameZero, kTransformToLocal);
	break;
	}

	return 1
}

var skipRotation = false 

// For the script to work with a menu with preview, you need to define a run method with the following three parameters:
// 1. api - render api to be used. Do not use RenderAPI directly, since preview uses a different renderer
// 2. parameter - contains model parameters. Do not refer directly to modelParameters inside the run method or methods called from it.
// 3. scale - preview can be scaled up or down depending on the size of the input pattern. Accomodate scale inside the run method.
function run (api, parameters, scale)
{
    // get the size of the output area  
    var outputSize = api.getParameter(kpsSize)
    // get the location of the top left corner of the bounding rectangle around the selected area
    var outputOrigin = api.getParameter(kpsOrigin)

    var patternSize = pattern.getParameter(kpsSize)
    var maxPatternSize = Math.max ( patternSize.x, patternSize.y) // before patternScale is applied
    patternSize *= parameters.patternScale
    
     if (parameters == previewParameters)
    {
        // set scale so that we show about 10-15 patterns
        if (maxPatternSize < previewSize / 5) 
            scale = previewSize / 5 / maxPatternSize
        else if (maxPatternSize > previewSize / 4)
            scale = previewSize / 4 / maxPatternSize
    }

   scale *= parameters.patternScale

    // scale up if the patternsize is 1 to avoid long loop
    if (patternSize.x == 1 && patternSize.y == 1)
    {
        if (scale == 1)
            scale = 20
        patternSize *= scale
        skipRotation = true    // 1x1 patterns that are scaled up do not rotate gracefully
    }

     // to finetune the progress bar, specify how many pixels are covered by patterns - the bigger the spacing the lower the value, for example
    api.setParameter(kpsPixelCoverage, 1.5)
 
    var patternModule = new PatternModule(parameters, scale, patternSize * scale);

    // Symmetry - module should be added to the system before modules that 
    // are part of the symmetry
    var symmetry = new Symmetry;

    // set the symmetry type
    setSymmetry (symmetry, parameters, patternSize * scale )


    Engine.addModule (symmetry)

    Engine.addModule (patternModule)

    // Add to the symmetry
    symmetry.addModule (patternModule)

    if (parameters.symmetryType >= 16)
        // Setting the bounding box like this will insure that neighboring fills are aligned for 2D tilings
        api.setSceneBBox (outputOrigin.x, outputOrigin.x + outputSize.x,  outputOrigin.y,  outputOrigin.y + outputSize.y)
    else
    {
        // Other symmetries will be centered around the center of the selection bounding box

        api.setSceneBBox (-outputSize.x/2,  outputSize.x/2,  -outputSize.y/2,  outputSize.y/2)
      }
 
    if (parameters == previewParameters)
    {
        api.pushMatrix()
        Engine.render (api)
        api.popMatrix()
     }
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Menu start
// If you want to create a menu in which you change some of the script parameters, include the following code:
// For shipped scripts we can include localized strings, prefixed with $$$/ - call method localize(string) on the prefixed string
// For your own strings, you can query app.locale and select several language versions (for example, your native language and english). Try alert ("Language is: " + app.locale)

var decoMenu = {    //  an object that defines the menu
   menuTitle : localize("$$$/DecoScripts/SymmetryFill/SymmetryFill=Symmetry Fill"),
   menuBackground : [0.93, 0.93, 0.93, 1],
   previewBackground : [1, 1, 1, 1],
   panels : [
    { panelName : "", 
       leftColumnWidth : 200,  
       unitsWidth : 65, 
       dropdownlistWidth : 200,
       panelMenu : [
         { itemName : localize("$$$/DecoScripts/SymmetryFill/SymmetryType=Symmetry type:"),  
             itemUnit : '', 
             itemType : 'dropdownlist',  itemList  : 
[
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry1= 1: line reflection"),  image : "Symmetry Fill/s0.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry2= 2: point reflection"),  image : "Symmetry Fill/s1.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry3= 3: rotation symmetry with 4 elements around the center"),  image : "Symmetry Fill/s2.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry4= 4: translation symmetry"),  image : "Symmetry Fill/s3.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry5= 5: glide reflection"),  image : "Symmetry Fill/s4.png" },

{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry6= 6: dilatation symmetry"),  image : "Symmetry Fill/s5.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry7= 7: dilative rotation symmetry"),  image : "Symmetry Fill/s6.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry8= 8: a different dilative rotation  symmetry"),  image : "Symmetry Fill/s7.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry9= 9: infinite dilative rotation symmetry"),  image : "Symmetry Fill/s8.png" },

// The Frieze tilings (1D tilings on an infinite line
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry10=10: translation frieze"),  image : "Symmetry Fill/s9.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry11=11: glide reflection frieze"),  image : "Symmetry Fill/s10.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry12=12: translation line reflection frieze"),  image : "Symmetry Fill/s11.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry13=13: translation mirror reflection frieze"),  image : "Symmetry Fill/s12.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry14=14: translation point reflection frieze"),  image : "Symmetry Fill/s13.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry15=15: translation double reflection frieze"),  image : "Symmetry Fill/s14.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry16=16: glide reflection rotation frieze"),  image : "Symmetry Fill/s15.png" },

// The Wallpaper tilings 2D tilings on an infinite plane
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry17=17: wallpaperP1 symmetry"),  image : "Symmetry Fill/s16.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry18=18: wallpaperP2 symmetry"),  image : "Symmetry Fill/s17.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry19=19: wallpaperPM symmetry"),  image : "Symmetry Fill/s18.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry20=20: wallpaperPG symmetry"),  image : "Symmetry Fill/s19.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry21=21: wallpaperPMM symmetry"),  image : "Symmetry Fill/s20.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry22=22: wallpaperCM symmetry"),  image : "Symmetry Fill/s21.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry23=23: wallpaperPMG symmetry"),  image : "Symmetry Fill/s22.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry24=24: wallpaperPGG symmetry"),  image : "Symmetry Fill/s23.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry25=25: wallpaperCMM symmetry"),  image : "Symmetry Fill/s24.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry26=26: wallpaperP4 symmetry"),  image : "Symmetry Fill/s25.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry27=27: wallpaperP4M symmetry"),  image : "Symmetry Fill/s26.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry29=28: wallpaperP4G symmetry"),  image : "Symmetry Fill/s27.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry30=29: wallpaperP3 symmetry"),  image : "Symmetry Fill/s28.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry31=30: wallpaperP3M1 symmetry"),  image : "Symmetry Fill/s29.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry32=31: wallpaperP31M symmetry"),  image : "Symmetry Fill/s30.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry33=32: wallpaperP6 symmetry"),  image : "Symmetry Fill/s31.png" },
{ item: localize("$$$/DecoScripts/SymmetryFill//Symmetry34=33: wallpaperP6M symmetry"),  image : "Symmetry Fill/s32.png" },
],
             itemValue : modelParameters.symmetryType, itemMin : 0, itemMax : 0, varName : 'symmetryType'  },
         
         { itemName : localize("$$$/DecoScripts/PatternScale=Pattern Scale:") ,  
             itemUnit :  "", itemType : 'slider', itemValue : modelParameters.patternScale, itemMin : 0.25, itemMax : 1.25, itemStep : 0.01, varName : 'patternScale'  }, 

        { itemName : localize("$$$/DecoScripts/SymmetryFill/PatternTranslationX=Pattern translation along width:") ,
             itemUnit : localize("$$$/DecoScripts/Units/percentOfWidth=% of width"),  
             itemType : 'slider', itemValue : modelParameters.patternTranslationX, itemMin : -100, itemMax : 100, itemStep : 1, varName : 'patternTranslationX'  }, 
         
          { itemName : localize("$$$/DecoScripts/SymmetryFill/PatternTranslationY=Pattern translation along height:") ,
             itemUnit : localize("$$$/DecoScripts/Units/percentOfHeight=% of height"),  
             itemType : 'slider', itemValue : modelParameters.patternTranslationY, itemMin : -100, itemMax : 100, itemStep : 1, varName : 'patternTranslationY'  }, 
         
          { itemName : localize("$$$/DecoScripts/ColorRandomness=Color randomness:"),  
             itemUnit : "",  itemType : 'slider', itemValue : modelParameters.colorRandomness, itemMin : 0, itemMax : 1, itemStep : 0.01, varName : 'colorRandomness'  }, 
         
          { itemName : localize("$$$/DecoScripts/BrightnessRandomness=Brightness randomness:"),  
             itemUnit : "",  itemType : 'slider', itemValue : modelParameters.brightnessRandomness, itemMin : 0, itemMax : 1, itemStep : 0.01, varName : 'brightnessRandomness'  }, 
       ] }
   ]  // end of panels
 }; // end of menu

// If livePreview is set to 1, the preview image is updated live. Note that due to limitations of scripted menus the update is slow and the flickering may be disturbing. 
livePreview = 0 // recommended value is 0

// Call Photoshop Script that creates the menu
Engine.evalFile ("_Deco Menu.jsx") 

// Menu finished
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


if (typeof skipRun == 'undefined' || !skipRun)  // run unles we exited the preview window without pressing a button
    run(RenderAPI, modelParameters, 1)
