package ONO::FW::Edu::Apps::Modules::Mandala;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2022-2026 Jos Kirps and The Joopita Project [ASBL]
# Copyright (C) 2011-2022 Jos Kirps and Joopita Research ASBL
# Copyright (C) 2006-2011 Jos Kirps and The Joopita Project
# Copyright (C) 2001-2006 Jos Kirps and The WobbleWolf Project
#
# This file, as well as many other parts of the ONO Software Project or
# related elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################
#: This module is a front end application module wrapper that has been
#: created for the Morzino Project (morzino.com) and that has been released
#: as Open Source software.
#:
#: Note that this is NOT a self-contained application, it is only a wrapper
#: that makes heavy use of the ONO Perl Framework application engine.
use strict;
use Encode;
use ONO::Render;
use ONO::ToolBox::Auth;
use ONO::Lib::Lang::LangKitEdu;
use ONO::Lib::DateTime::ToolBox;
use ONO::FW::Apps::Core;
use ONO::FW::Apps::Core::Graphics;
use ONO::FW::Edu::Apps::ToolBox;
use ONO::FW::User::Init;
use ONO::FW::Edu::Apps::Modules::Drawing;
use ONO::Lib::Image::Drawing;
use ONO::IO;
use ONO::DB;
use ONO::Lib::Basic;
use ONO::Lib::UI::Range;
sub play {
my (
$self,
$option,
$vars_ref,
$HTTP,
$BASE,
) = @_;
################################################################
# INIT - BEGIN
################################################################
my (
$db,
$community,
$action_url,
$form,
$format,
$canvas,
$pdfkey,
$sec,$min,$hour,
$mday,$mon,$year,
$wday,$yday,$timestamp,
$lang,
$BLK_ref,
$vars_ref,
) = ONO::FW::Apps::Core->init($vars_ref);
my %vars = %$vars_ref;
my %BLK = %$BLK_ref;
my ($HEAD,$WEB);
my (
$vpath,
$lang,$domain,$community,
$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,
$script_url,$action_url,$form
) = ONO::FW::User::Init->getvar(\%vars);
ONO::FW::Apps::Core->now("","chess",$vars{'app_lang'});
my $action_url = qq~$ENV{'SCRIPT_NAME'}?app_lang=$vars{'app_lang'}&~;
###################################################################
# MANDALA
###################################################################
my $CSSJS = ONO::Render->cssjs($BASE);
my ($width,$height) = (660,660);
if ($vars{'username'} && $vars{'ono_lib_image_drawing_target_folder'} && $vars{'ono_lib_image_drawing_data'} =~ s~^data:image/png;base64,~~) {
my $FILE;
if ($vars{'ono_lib_image_drawing_target_folder'} eq "user_documents_images") {
$FILE = "media/users/".ONO::IO->deepdir($vars{'username'})."/$vars{'username'}/documents/images";
}
if ($vars{'ono_lib_image_drawing_target_folder'} =~ m~^virclass_(.*?)_class$~) {
my $virclass = $1;
$virclass =~ s~[^0-9]~~gi;
foreach my $line (ONO::DB->select($db,"${community}_school_virclass_relationships","username = '$vars{'username'}' AND virclass = '$virclass'")) {
my @col = ONO::DB->readcols($line);
$FILE = "media/virclass/$virclass/class/$vars{'username'}/documents/images";
}
}
if ($FILE) {
$vars{'ono_lib_image_drawing_save_success'} = ONO::Lib::Image::Drawing->save("$FILE/$vars{'ono_lib_image_drawing_filename'}.png",$vars{'ono_lib_image_drawing_data'});
}
}
my @txts = ONO::Lib::Image::Drawing->txts($lang);
my @txt = (
"start auto-draw",
"stop auto-draw",
);
if ($lang eq "de") {
@txt = (
"Automatik ein",
"Automatik aus",
);
}
if ($lang eq "lu") {
@txt = (
"Automatik un",
"Automatik aus",
);
}
if ($lang eq "fr") {
@txt = (
"dessin automatique",
"dessin automatique",
);
}
my $SUCCESS;
if ($vars{'ono_lib_image_drawing_save_success'}) {
$SUCCESS = ONO::Lib::Image::Drawing->success($BLK{'Done'},$txts[3],$BLK{'ok'});
}
my $DIRS = ONO::FW::Edu::Apps::Modules::Drawing->get_dirs($db,$community,$vars{'username'});
my $SAVE = ONO::Lib::Image::Drawing->savebox(\@txts,$BLK_ref,$vars_ref,"",$DIRS,"mandala");
my $SAVE2;
my $SAVEBUTTON = ONO::Lib::Image::Drawing->savebutton("$BLK{'save'} / $BLK{'download'}","button_green button_small mt5");
my $SAVEBOXES = ONO::Lib::Image::Drawing->saveboxes($SAVE,$SAVE2,$lang,$BLK_ref,$BASE);
my $SAVESCRIPT = ONO::Lib::Image::Drawing->savescript_generic("defaultCanvas0");
my $CLEARBUTTON = ONO::Lib::Image::Drawing->clearbutton($BLK{'clear'},"button_orange button_small mt5");
my $CLEARBOX = ONO::Lib::Image::Drawing->clearbox("clear_canvas();",$BLK_ref);
my $copies = 6+2*int(rand(8));
my $trans = 70+int(rand(30));
my ($r,$g,$b) = (int(rand(160)),int(rand(160)),int(rand(160)));
my $RANGE_COPIES = ONO::Lib::UI::Range->range("sCopies",$copies,1,100,1,$BLK{'Copies'});
my $RANGE_WIDTH = ONO::Lib::UI::Range->range("ESZ",1,0.1,5,0.1,$BLK{'Width'});
my $RANGE_TRANS = ONO::Lib::UI::Range->range("sTRANSP",$trans,0,100,1,$BLK{'Transparency'});
my $RANGE_RED = ONO::Lib::UI::Range->range(
"sRRR:color_mixer",$r,0,255,1,$BLK{'Red'}, "JR",
"onojs_setbgcolorrgb('color_mixer',onojs_getvalue('ono_range_sRRR_id'),onojs_getvalue('ono_range_sGGG_id'),onojs_getvalue('ono_range_sBBB_id'));",
);
my $RANGE_GREEN = ONO::Lib::UI::Range->range(
"sGGG:color_mixer",$g,0,255,1,$BLK{'Green'}, "JG",
"onojs_setbgcolorrgb('color_mixer',onojs_getvalue('ono_range_sRRR_id'),onojs_getvalue('ono_range_sGGG_id'),onojs_getvalue('ono_range_sBBB_id'));",
);
my $RANGE_BLUE = ONO::Lib::UI::Range->range(
"sBBB:color_mixer",$b,0,255,1,$BLK{'Blue'}, "JB",
"onojs_setbgcolorrgb('color_mixer',onojs_getvalue('ono_range_sRRR_id'),onojs_getvalue('ono_range_sGGG_id'),onojs_getvalue('ono_range_sBBB_id'));",
);
my $MANDALA = qq~
<div class="rel">
<div class="bo rel" style="width:660px;height:450px;overflow:hidden"><div class="container abs" id="canvas" style="top:-105px"></div></div>
<div class="container">
$CLEARBOX
<div id="mandala_start_box" class="box_green abs hide" style="top:100px;right:150px;z-index:999;min-width:240px">
<div class="inline auto"><h3>Mandala</h3></div>
<div class="box_paper">
<div class="inline auto">
<a href="javascript:void(0);" onclick="onojs_hide('mandala_start_box');onojs_class('mandala_ui','abs');use_particle_off();clear_canvas();" class="button_green button_big">$BLK{'start'}</a>
</div>
</div>
</div>
<div id="mandala_ui" class="abs" style="top:-10px;left:654px;width:242px;height:460px">
<div class="ml20 mt10 lh125">
<div class="col-sm-4" id="settings">
<div class="box_paper mb5">
<table class="wide_table">
<tr class="vtop">
<td class="p0">
<div class="mr5"><div id="color_mixer" class="block32 bg_white bo radius32" style="background-color:rgb($r,$g,$b);"></div></div>
</td>
<td class="p0">
$RANGE_RED
$RANGE_GREEN
$RANGE_BLUE
</td>
</tr>
</table>
</div>
<div class="pad10_0">
$RANGE_COPIES
$RANGE_WIDTH
$RANGE_TRANS
</div>
</div>
<div class="col-sm-4 hide" id="psettings"></div>
<div class="inline mt10">
<div id="use_particle_button_on">
<a href="javascript:void(0);" onclick="use_particle_off();onojs_block('use_particle_button_off');onojs_hide('use_particle_button_on');" class="button_red button_small">$txt[1]</a>
</div>
<div id="use_particle_button_off" class="hide">
<a href="javascript:void(0);" onclick="use_particle_on();onojs_block('use_particle_button_on');onojs_hide('use_particle_button_off');" class="button_green button_small">$txt[0]</a>
</div>
<div class="inline w100">
$CLEARBUTTON
</div>
<div class="inline w100">
$SAVEBUTTON
</div>
</div>
</div>
</div>
$SAVEBOXES
<input type="hidden" name="ono_lib_image_drawing_data" id="ono_lib_image_drawing_data_id" class="w95">
$SUCCESS
$SAVESCRIPT
</div>
</div>
<script>
var BG_color = '#ffffff';
var prevx = 0;
var prevy = 0;
var p_prevx = 0;
var p_prevy = 0;
var lining = false;
var lining_base = true;
var particle_mode = false;
var particle_rt = 0.5;
var particle_theta = 0;
var polygon_projection = false;
function setup() {
var cnv = createCanvas($width,$height);
cnv.parent('canvas');
background(BG_color);
noStroke();
sCOPIES = document.getElementById('ono_range_sCopies_number');
sESZ = document.getElementById('ono_range_ESZ_number');
sTRANSP = document.getElementById('ono_range_sTRANSP_number');
sRRR = document.getElementById('ono_range_sRRR_number');
sGGG = document.getElementById('ono_range_sGGG_number');
sBBB = document.getElementById('ono_range_sBBB_number');
sBG_color = createSlider(0,255,BG_color,1);
sBG_color.parent('psettings');
sFade = createSlider(0,255,0,1);
sFade.parent('psettings');
cnvText = createP('Canvas size');
cnvText.parent('psettings');
cnvSlider = createSlider(250,1000,600,1);
cnvSlider.parent('psettings');
cnvSlider.changed(cnvChangeEvent);
sFaces = createSlider(3,15,4,1);
sFaces.parent('psettings');
radText = createP('Radius (min,max,variation frequency)');
sRtmin = createSlider(-0.5,0.5,-0.25,0.01);
sRtmax = createSlider(0.25,2,1.5,0.01);
sRfreq = createSlider(0.0001,0.02,0.01,0.0001);
radText.parent('psettings');
sRtmin.parent('psettings');
sRtmax.parent('psettings');
sRfreq.parent('psettings');
thetaText = createP('Angle variation (min,max,variation frequency)');
sThetamin = createSlider(-3,0.5,-1,0.01);
sThetamax = createSlider(-0.5,3,1,0.01);
sThetafreq = createSlider(0.0001,0.05,0.01,0.0001);
thetaText.parent('psettings');
sThetamin.parent('psettings');
sThetamax.parent('psettings');
sThetafreq.parent('psettings');
}
function cnvChangeEvent() {
var sz = cnvSlider.value();
cnv = createCanvas(sz,sz);
cnv.parent('canvas');
background(sBG_color.value());
console.log(sz);
}
function clear_canvas() {
background(BG_color);
}
function canvas_save() {
saveCanvas('myCanvas', 'png');
}
function invert_colors() {
filter(INVERT);
}
function connexion() {
lining_base = !lining_base;
}
function use_particle() {
particle_mode = !particle_mode;
}
function use_particle_on() {
particle_mode = true;
}
function use_particle_off() {
particle_mode = false;
}
function use_polygon() {
polygon_projection = !polygon_projection;
}
function reset() {
location.reload();
seedRandom();
seedNoise();
}
function draw_point() {
if (mouseX>=0 && mouseY>=0 && mouseX<=width && mouseY<=height) {
var c = color(sRRR.value,sGGG.value,sBBB.value,parseInt(sTRANSP.value));
mx2 = mouseX - width/2;
my2 = mouseY - height/2;
var dist = sqrt(mx2*mx2 + my2*my2);
var theta0 = (atan2(my2,mx2)+TWO_PI)%(TWO_PI);
var realtheta0 = (atan2(my2,mx2)+PI)%PI;
dist = dist*cos((realtheta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
noStroke();
fill(c);
if (!polygon_projection) {
for(var i=0;i<sCOPIES.value;i++){
var theta = i*TWO_PI/sCOPIES.value;
var xp = mx2*cos(theta) - my2*sin(theta);
var yp = mx2*sin(theta) + my2*cos(theta);
ellipse(xp+width,yp+height,sESZ.value);
}
} else {
for(var i=0;i<sCOPIES.value;i++){
var theta = theta0 + i*TWO_PI/sCOPIES.value;
var dist2 = dist*cos((theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
var xp = dist2*cos(theta);
var yp = dist2*sin(theta);
ellipse(xp+width,yp+height,sESZ.value);
}
}
prevx = mx2;
prevy = my2;
prevdist = dist;
prevtheta0 = theta0;
}
}
function draw_point_particle() {
var c = color(sRRR.value,sGGG.value,sBBB.value,parseInt(sTRANSP.value));
mx2 = width/2*particle_rt*cos(particle_theta);
my2 = height/2*particle_rt*sin(particle_theta);
var dist = sqrt(mx2*mx2 + my2*my2);
var theta0 = (atan2(my2,mx2)+TWO_PI)%(TWO_PI);
var realtheta0 = (atan2(my2,mx2)+PI)%PI;
dist = dist*cos((realtheta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
noStroke();
fill(c);
if (!polygon_projection) {
for(var i=0;i<sCOPIES.value;i++){
var theta = i*TWO_PI/sCOPIES.value;
var xp = mx2*cos(theta) - my2*sin(theta);
var yp = mx2*sin(theta) + my2*cos(theta);
ellipse(xp+width,yp+height,sESZ.value);
}
} else {
for(var i=0;i<sCOPIES.value;i++){
var theta = theta0 + i*TWO_PI/sCOPIES.value;
var dist2 = dist*cos((theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
var xp = dist2*cos(theta);
var yp = dist2*sin(theta);
ellipse(xp+width,yp+height,sESZ.value);
}
}
p_prevx = mx2;
p_prevy = my2;
p_prevdist = dist;
p_prevtheta0 = theta0;
}
function draw_line() {
if (!lining) {
draw_point();
if (mouseX>=0 && mouseY>=0 && mouseX<=width && mouseY<=height) lining = lining_base;
} else {
var c = color(sRRR.value,sGGG.value,sBBB.value,parseInt(sTRANSP.value));
mx2 = mouseX - width/2;
my2 = mouseY - height/2;
var dist = sqrt(mx2*mx2 + my2*my2);
var theta0 = (atan2(my2,mx2)+TWO_PI)%(TWO_PI);
var realtheta0 = (atan2(my2,mx2)+PI)%PI;
dist = dist*cos((realtheta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
//dist = dist*cos((realtheta0+PI/4)%(PI/2)-PI/4)/cos((theta0+PI/4)%(PI/2)-PI/4);
fill(c);
strokeWeight(sESZ.value);
stroke(c);
if (!polygon_projection) {
for(var i=0;i<sCOPIES.value;i++){
var theta = i*TWO_PI/sCOPIES.value;
var xp = mx2*cos(theta) - my2*sin(theta);
var yp = mx2*sin(theta) + my2*cos(theta);
var xp2 = prevx*cos(theta) - prevy*sin(theta);
var yp2 = prevx*sin(theta) + prevy*cos(theta);
line(xp2+width,yp2+height,xp+width,yp+height);
}
} else {
for(var i=0;i<sCOPIES.value;i++){
var theta = theta0 + i*TWO_PI/sCOPIES.value;
var dist2 = dist*cos((theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
var dist3 = prevdist*cos((prevtheta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta + prevtheta0 - theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
var xp = dist2*cos(theta);
var yp = dist2*sin(theta);
var xp2 = dist3*cos(theta + prevtheta0 - theta0);
var yp2 = dist3*sin(theta + prevtheta0 - theta0);
line(xp2+width,yp2+height,xp+width,yp+height);
}
}
prevx = mx2;
prevy = my2;
prevdist = dist;
prevtheta0 = theta0;
lining = lining_base;
}
}
function draw_line_particle() {
if (!lining_particle) {
draw_point_particle();
lining_particle = lining_base;
} else {
var c = color(sRRR.value,sGGG.value,sBBB.value,parseInt(sTRANSP.value));
mx2 = width/2*particle_rt*cos(particle_theta);
my2 = height/2*particle_rt*sin(particle_theta);
fill(c);
strokeWeight(sESZ.value);
stroke(c);
var dist = sqrt(mx2*mx2 + my2*my2);
var theta0 = (atan2(my2,mx2)+TWO_PI)%(TWO_PI);
var realtheta0 = (atan2(my2,mx2)+PI)%PI;
dist = dist*cos((realtheta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
if (!polygon_projection) {
for(var i=0;i<sCOPIES.value;i++){
var theta = i*TWO_PI/sCOPIES.value;
var xp = mx2*cos(theta) - my2*sin(theta);
var yp = mx2*sin(theta) + my2*cos(theta);
var xp2 = p_prevx*cos(theta) - p_prevy*sin(theta);
var yp2 = p_prevx*sin(theta) + p_prevy*cos(theta);
line(xp2+width,yp2+height,xp+width,yp+height);
}
} else {
for(var i=0;i<sCOPIES.value;i++){
var theta = theta0 + i*TWO_PI/sCOPIES.value;
var dist2 = dist*cos((theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
var dist3 = p_prevdist*cos((p_prevtheta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value())/cos((theta + p_prevtheta0 - theta0+PI/sFaces.value())%(PI/sFaces.value()*2)-PI/sFaces.value());
var xp = dist2*cos(theta);
var yp = dist2*sin(theta);
var xp2 = dist3*cos(theta + p_prevtheta0 - theta0);
var yp2 = dist3*sin(theta + p_prevtheta0 - theta0);
line(xp2+width,yp2+height,xp+width,yp+height);
}
}
p_prevx = mx2;
p_prevy = my2;
p_prevdist = dist;
p_prevtheta0 = theta0;
lining_particle = lining_base;
}
}
function draw() {
background(sBG_color.value(),(sFade.value()/255.0)*(sFade.value()/255.0)*255);
translate(-width/2,-height/2);
if (mouseIsPressed) {
draw_line();
} else {
lining = false;
}
if (particle_mode) {
draw_line_particle();
} else {
lining_particle = false;
}
particle_rt = lerp(sRtmin.value(),sRtmax.value(),noise(sRfreq.value()*frameCount))/1.5;
particle_theta += lerp(sThetamin.value(),sThetamax.value(),noise(sThetafreq.value()*frameCount + 1000))/sCOPIES.value;
}
</script>
~;
print qq~Content-Type: text/html; charset=UTF-8\n\n
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="Generator" content="ONO App Engine">
$CSSJS
<script language="javascript" src="$BASE/ono/osr/javascript/p5/0.5.2/p5.js"></script>
<script language="javascript" src="$BASE/ono/osr/javascript/p5/0.5.2/p5.dom.js"></script>
</head>
<body style="margin:0px;padding:0px" onload="use_particle();">
<!-- This code is based on sketch_js by Etienne Jacob (etin (dot) jacob (at) gmail (dot) com) -->
<!-- Code reused under license by Etienne Jacob, modified for Morzino/ONO by Jos F. Kirps -->
<form id="ono_lib_image_drawing_form" method="post">
<input type="hidden" name="app_lang" value="$vars{'app_lang'}">
<div class="box_fabric flat_all">$MANDALA</div>
</form>
</body>
</html>
~;
}
###################################################################
#### THAT'S IT :-D
###################################################################
1;
__END__