Community RSS Feed
http://community.wolfram.com
RSS Feed for Wolfram Community showing any discussions in tag Dynamic Interactivity sorted by activePlot an epicycloid using a ParametricPlot?
http://community.wolfram.com/groups/-/m/t/1165072
Consider the following code:
epicycloid[tend_] :=
Module[{a = 3, b = 1, p = 1, x, y},
x[t_] := (a + b) Cos[t] - b p Cos[(a + b) t/b];
y[t_] := b (a + b) Sin[t] - b p Sin[(a + b) t/b];
ParametricPlot[{x[t], y[t]}, {t, 0, tend},
PlotStyle -> {Red, Thick}, Ticks -> None, ImageSize -> 300,
Epilog -> {Thick, Blue, Circle[{0, 0}, a],
Black, Circle[{x[tend, 0], y[tend, 0]}, b], Red,
PointSize[Large], Point[{x[tend], y[tend]}], Black,
Line[{{x[tend, 0], y[tend, 0]}, {x[tend], y[tend]}}]},
PlotRange -> (a + b) (1 + 1/5) {-1, 1.1}]];
Manipulate[Quiet[epicycloid[tend]], {tend, 0.0001, 2 \[Pi]}]Lisan Lisov2017-08-15T06:04:01Z[✓] Plot the circles within a Manipulate?
http://community.wolfram.com/groups/-/m/t/1164758
Consider the following code:
hypocycloid[tend] :=
Module[{a = 4, b = 1, x, y, p},
x[t_, p_] := b (a/b - 1) Cos[t] + b p Cos[(a/b - 1) t];
y[t_, p_] := b (a/b - 1) Sin[t] - b p Sin[(a/b - 1) t];
ParametricPlot[{x[t, 1], y[t, 1]}, {t, 0, tend},
PlotStyle -> {Red, Thick}, Ticks -> None, ImageSize -> 225,
Epilog -> {Thick, Blue, Circle[{0, 0}, a], Black,
Circle[{x[tend, 0], y[tend, 0]}, b], Red, PointSize[Large],
Point[{x[tend, 1], y[tend, 1]}], Black,
Line[{{x[tend, 0], y[tend, 0]}, {x[tend, 1], y[tend, 1]}}]},
PlotRange -> (1 + 1/20) {-a, a}]];
Manipulate[Quiet@hypocycloid[tend], {tend, .0001, 2*Pi}]Lisan Lisov2017-08-14T11:04:42Z[✓] Use DynamicModule for better presentation?
http://community.wolfram.com/groups/-/m/t/1163875
Hi
I have the program for solving equations by FDM & FEM , so i need the program in DynamicModule that show both FDM & FEM results to see the difference between them , and i want to present them by DynamicModule for better & professional presenting , can U help me to find my errors on program that i write in DynamicModule?
Thank You
"FDM Code"
ClearAll["Global`*"];
n = Input["n"];
m = n - 1; \[Alpha] = -4 + h^2; \[Beta] = 6 - 2 h^2 + h^4; h = 1/n;
Do[Do[a[i, j] = 0, {i, 1, m}], {j, 1, m}];
Do[b[j, 1] = 0, {j, 1, m}]; b[m, 1] = -9 - 3 \[Alpha] - 5 h^3;
b[1, 1] = 3 + 2 h - h^2; b[2, 1] = -1;
b[m - 1, 1] = -3; If[n == 4, b[2, 1] = -4, b[2, 1] = -1];
Do[a[j, j] = \[Beta], {j, 1, m - 1}];
Do[a[j, j] = \[Beta] - 3, {j, m, m}];
Do[a[j, j + 1] = \[Alpha], {j, 1, m - 1}];
Do[a[j + 1, j] = \[Alpha], {j, 1, m - 1}];
Do[a[j + 1, j] = \[Alpha] + 1, {j, m - 1, m}];
Do[a[j, j + 2] = 1., {j, 1, m - 2}];
Do[a[j + 2, j] = 1., {j, 1, m - 2}];
A = Array[a, {m, m}];
B = Array[b, {m, 1}];
Y = Flatten[LinearSolve[A, B]];
ListPlot[{Y}, Joined -> True, PlotMarkers -> Automatic]
"FEM Code"
DSolve[{u''''[x] + u''[x] + u[x] == 0, u[0] == 1, u'[0] == 2,
u[1] == 3, u'''[1] == 5}, u[x], x];
(*Plot[u[x]/.%,{x,1,2}]*)
u[x_] = u[x] /. %;
{u[0.25], u[0.5], u[0.75]}
"DynamicModule Code "
DynamicModule[
m = n - 1; \[Alpha] = -4 + h^2; \[Beta] = 6 - 2 h^2 + h^4; h = 1/n;
Do[Do[a[i, j] = 0, {i, 1, m}], {j, 1, m}];
Do[b[j, 1] = 0, {j, 1, m}]; b[m, 1] = -9 - 3 \[Alpha] - 5 h^3;
b[1, 1] = 3 + 2 h - h^2; b[2, 1] = -1; b[m - 1, 1] = -3;
If[n == 4, b[2, 1] = -4, b[2, 1] = -1];
Do[a[j, j] = \[Beta], {j, 1, m - 1}];
Do[a[j, j] = \[Beta] - 3, {j, m, m}];
Do[a[j, j + 1] = \[Alpha], {j, 1, m - 1}];
Do[a[j + 1, j] = \[Alpha], {j, 1, m - 1}];
Do[a[j + 1, j] = \[Alpha] + 1, {j, m - 1, m}];
Do[a[j, j + 2] = 1., {j, 1, m - 2}];
Do[a[j + 2, j] = 1., {j, 1, m - 2}];
A = Array[a, {m, m}];
B = Array[b, {m, 1}];
Y = Flatten[LinearSolve[A, B]]; {z = 0.00001}, {Panel[
SetterBar[Dynamic[n], Range[4, 20]],
Dynamic[ListPlot[{Y}, Joined -> True, PlotMarkers -> Automatic]]]}]Saleh Baradaran2017-08-12T13:59:25Z[GIF] Grassfire (Parallels of the limaçon trisectrix)
http://community.wolfram.com/groups/-/m/t/1158782
![Parallels of the limaçon trisectrix][1]
**Grassfire**
An interesting thing I noticed while playing around with the [grassfire transform][2], which just pushes a curve off itself to get parallel copies: the [limaçon trisectrix][3] is self-parallel, meaning that there is a special distance (namely $\sqrt{\frac{207}{64} + \frac{3 \sqrt{3}}{4}}$) so that if you travel exactly that distance in a direction normal to every point on the limaçon, you get a congruent copy of the limaçon.
I did some searching to try to determine who first noticed this, but couldn't find anything. Does anybody have a good reference?
Here's the code, where $f$ parametrizes the curve and $g$ gives the normal to the curve:
DynamicModule[{f, g, rmax = Sqrt[207/64 + (3 Sqrt[3])/4], n = 400,
m = 200, cols = RGBColor /@ {"#4E1184", "#FD367E", "#0E1555"}},
f[t_] := {-(1 - 2 Cos[t]) Sin[t], (1 - 2 Cos[t]) Cos[t]};
g[t_] = {-#[[2]], #[[1]]} &[Normalize[D[f[t], t]]];
Manipulate[
Graphics[
{EdgeForm[Directive[cols[[1]], Thickness[.002]]], FaceForm[None],
Table[
{EdgeForm[Directive[Opacity[n/(m*rmax) (s - r) + 1], Blend[cols[[;; 2]], n/(m*rmax) (s - r) + 1]]],
Polygon[Table[f[t] + s g[t], {t, 0., 2 π, 2 π/200}]]},
{s, Max[r - m * rmax/n, 0], Min[r, rmax], rmax/n}]},
Axes -> None, ImageSize -> 540, Background -> cols[[-1]],
PlotRange -> {{-(3 + rmax)/2, (3 + rmax)/2}, {-3.5, rmax - 1 + 1/2}}],
{r, 0., rmax + m*rmax/n}]
]
[1]: http://community.wolfram.com//c/portal/getImageAttachment?filename=parallels18c.gif&userId=610054
[2]: https://en.wikipedia.org/wiki/Grassfire_transform
[3]: https://en.wikipedia.org/wiki/Lima%C3%A7on_trisectrixClayton Shonkwiler2017-08-05T02:42:32ZCastlevania Stage 1 Demo
http://community.wolfram.com/groups/-/m/t/1161057
Update 1: added examples about side-scrolling and the reason for a time-scale factor.
Since making [Flappy Bird in the Wolfram Language][1], I decided to up the ante with a playable demo of a video game classic, Castlevania for the Nintendo Entertainment System. To limit the scope of the project, I only recreated the first stage, but modified it to play like "horde mode" where enemies continuously spawn:
![Gameplay][2]
The package can be downloaded from my github account: [CastlevaniaDemo_WolframLanguage][3].
# Controls #
I wanted to avoid using keyboard events since they block, i.e. you can't hold the right-arrow key and press another key (to jump) at the same time. Instead, I use an Xbox controller because I'm on a Windows computer and it has the device drivers already installed. It makes connecting the controller as easy as turning in on. If you want to use a different controller, then you may need to re-map some of the [`ControllerState`][4]s.
![enter image description here][5]
I tried to mimic the original controls where you can move left and right, as well as crouch or stand using the directional pad. Only two buttons are used: one to jump and one to crack the whip. The crouch doesn't help much in such a simple level, but it's there nonetheless.
# Design #
## Side-scrolling ##
----------
A 2-D side-scroller moves the screen as the player advances. This is straightforward to achieve using a dynamic [`PlotRange`][6]. I found the image from a basic Google search, cropped it, and used [`Inset`][7] to place it in a [`Graphics`][8] expression. To put everything in an understandable coordinate system, I adjusted the relative size (4.19) until the image spanned the vertical space from 0 to 1. The single static image `background` is
![Background][9]
Side-scrolling is implemented by updating the [`PlotRange`][10] dynamically over an otherwise static graphic:
Manipulate[
Graphics[
Inset[background, {0, 0}, Scaled[{0, 0}], 4.19],
Frame -> True, PlotRangeClipping -> True,
PlotRange -> {{Dynamic[scrollPos], Dynamic[screenWidth + scrollPos]}, {0, 1}}
],
{{scrollPos, 0, "Screen Position"}, 0, 3},
{{screenWidth, 1, "Screen Width"}, 1, 2}
]
![Scroll Position][11]
The above code is a demonstration, whereas the game updates `scrollPos` as the player moves. There are additional restrictions based on where the player is located, such that the player can walk to the edge of the screen, but the screen stops moving when the player is close.
In the following, `csX` is the x-direction on the directional pad. Its value is +1 if held to the right and -1 if held to the left.
Attributes[moveRight] = {HoldRest};
moveRight[csX_, fps_] := (
(* face player in direction of movement *)
If[!playerFacingRight, playerFacingRight=True];
(* update player sprite and screen position *)
With[{p=playerX+(playerXvel/fps)*csX}, If[p < 3.55, playerX=p]];
If[scrollPos < 2.7 && ((scrollPos+screenWidth)/2.4 < playerX), scrollPos+=(scrollSpeed/fps)]
);
Attributes[moveLeft] = {HoldRest};
moveLeft[csX_, fps_] := (
(* face player in direction of movement *)
If[playerFacingRight, playerFacingRight=False];
(* update player sprite and screen position *)
With[{p=playerX+(playerXvel/fps)*csX}, If[0.07 < p, playerX=p]];
If[scrollPos > 0 && (scrollPos+screenWidth/2.3 > playerX), scrollPos-=(scrollSpeed/fps)]
);
## Controlling Update Speed ##
----------
Instead of using a [`ScheduledTask`][12] to update at a fixed frame rate, I opted to let [`Dynamic`][13] update as fast as possible. This is reminiscent of old computer games that were tied to the processor's frequency. To get around this I use a global variable `fps` to slow down the apparent motion. Said differently, the front end updates as fast as possible, but the distance a sprite moves per kernel-state-update is proportional to `fps`.
The previous section shows `fps` in the player update:
p = playerX + (playerXvel / fps)*csX
The player's horizontal-position `playerX` is updated by the fixed horizontal-velocity `playerXvel`, but proportional to `fps`. If `fps` is larger, then the distance traveled per-kernel-update is smaller.
For every moving sprite, however, the front end has to communicate more with the kernel. This causes the entire game to slow down proportionally to the number of dynamically updating expressions. I more-or-less get around this detail by linearly adjusting the global `fps` variable proportional to the number of active sprites. The rough idea is that if each sprite takes 2 units of `fps` time to update, then I should take away 2 units for each active enemy.
As a toy example of this behavior, look at the following independent example:
fps = 10;
objList = <||>;
createShape[index_Integer] := Module[{pos = RandomReal[{-30, 0}, 2]},
object[index] := (
(* updated object position *)
pos += 0.3/fps;
(* if object leaves the screen, remove it from the list of objects, otherwise output graphic *)
If[pos.pos > 30^2,
objList = KeyDrop[objList, index]; object[index] =.,
{EdgeForm[Directive[Thick]], FaceForm[None], Rectangle[pos, pos + {5, 5}]}
]
);
objList = <|objList, index :> object[index]|>
]
i = 1;
Column[{
Button["Add", createShape[i++]],
Graphics[Dynamic[Values[objList]],
PlotRange -> {{-30, 30}, {-30, 30}}, Frame -> True,
ImageSize -> Medium],
Dynamic[Length[objList]]
}]
![enter image description here][14]
Ignoring that this code produces a front-end memory leak, what you should observe is that the rectangles motion slows down as more are added. Once a few rectangles leave the "world", the remaining rectangles speed up.
In my game code where I create enemies, I use a variable `tsf` (time-scale-factor) which is just a local renaming of `fps`. It decreases when an enemy is put in motion i.e. initialized, and increases when the enemy dies or leaves the screen. Below I only show the parts where `tsf` is adjusted. `speedTrigger` is used as a flag to prevent `tsf` from updating more than once.
initializeEnemy[index, "ghoulRight"] := (
. . .
speedTrigger=True; tsf-=2;
. . .
enemy[index] := (
If[ !dying
(* if exited map *)
If[xPos > 3.8,
. . .
If[speedTrigger, tsf+=2; speedTrigger = False];
. . .
],
(* if dead, after death animation, position enemy off screen, update score *)
If[speedTrigger, tsf+=2; speedTrigger = False];
. . .
]
)
)
## Sprites before hit boxes ##
----------
It makes more sense to place and size the sprites into the graphics before I create hit boxes. Sprite maps can be [found online][15] for many classic games. I found a character map for the main character sprite and cropped each frame. There are 12 unique frames in total for my chosen character movement, but for smooth animations some frames can be found in multiple sequences:
- four frames for walking
![enter image description here][16]
- four frames for whipping while standing
![enter image description here][17]
- four frames for whipping while crouched
![enter image description here][18]
- three frames for character death
![enter image description here][19]
The above images have already been adjusted. First, I applied [`RemoveBackground`][20] and [`ImageCrop`][21] to each frame. If you naively stopped here and simply updated the first argument of [`Inset`][22] with these images, then your character would appear glitchy when switching between images. The reason is that the inset images are of different sizes. I needed to "normalize" the images to a common size.
The whip frames have the largest extent (see the above images that are selected). The whip hangs below the feet while crouched. It also extends far to the left/right when fully extended. I chose common image size of 105 x 42. I first used [`ImagePad`][23] to pad each image up to the chosen size. Then I created functions for fine-tuning the sprite positions:
imageRotateLeft[im_, amount_] := If[amount == 0,
im,
ImageAssemble[{{ImageTake[im, {1, -1}, {1 + amount, -1}], ImageTake[im, {1, -1}, {1, amount}]}}]
]
imageRotateRight[im_, amount_] := If[amount == 0,
im,
ImageAssemble[{{ImageTake[im, {1, -1}, {-amount, -1}], ImageTake[im, {1, -1}, {1, -1 - amount}]}}]
]
I use a combination of [`Manipulate`][24] expressions to align and center each frame. One aligns one frame with another:
Manipulate[
left = tt[[1]]; right = tt[[2]];
Framed[ImageCompose[left, imageRotateLeft[right, n]], FrameMargins -> None],
{n, 0, 10, 1}
]
![enter image description here][25]
The other aligns a frame with its reflection:
Manipulate[
left = tt[[m]]; right = ss[[m]];
Framed[ImageCompose[imageRotateRight[left, n], imageRotateLeft[right, n]], FrameMargins -> None],
{n, 0, 10, 1}, {m, 1, 4, 1}
]
![Reflection Alignment][26]
Having a reflection-aligned set of frames is useful when making hit boxes. That way we only need one hit box for standing and one for crouching, regardless of facing right or left. Moreover, having "one-size-fits-all" for the images, the inset size argument remains fixed.
The ghoul enemy proceeds similarly, but is simpler since it is only two animation frames and does not include a moving weapon.
## Character Controls ##
----------
I use a [`Module`][27] to mimic "static" variables for the player character.
Module[{playerFacingRight=True, playerCrouched=False, playerX=0.288, playerY=0.282,
playerXvel=0.2, scrollSpeed=0.2, im=walkingRight[[1]],
playerAnimationCounter=1, playerFrame=1, walkAnimationDelay=25,
grav=0.5, jumpVel=0.5, playerYvel=0, jumpCounter=0, previousJumpState=False,
whipFrame=1, whipAnimationCounter=1, previousWhipState=False, whipping=False, whipAnimationDelay=8,
whipStandLeftBox,whipStandRightBox,whipCrouchedLeftBox,whipCrouchedRightBox,
crouchedBox,standingBox,playerBox,whipSoundCheck=True},
Attributes[standWhipUpdate] = {HoldAll};
standWhipUpdate[whipBox_] := ( . . . )
Attributes[crouchWhipUpdate] = {HoldAll};
crouchWhipUpdate[whipBox_] := ( . . . )
Attributes[moveRight] = {HoldRest};
moveRight[csX_, fps_] := ( . . . )
Attributes[moveLeft] = {HoldRest};
moveLeft[csX_, fps_] := ( . . . )
Attributes[character] = {HoldRest};
character[{csX_,csY_,csB_, csB2_}, enemyBox_, whipBox_, fps_, playerDead_] := (
.
.
.
Inset[im, {playerX,playerY}, Scaled[{0.5,0.5}], 0.562]
)
]
Within this module I define a number of functions that share the scoped module variables. The main function is `character` that, after processing the current state of the player, returns the `Inset` graphics primitive.
The `cs*` inputs are `ControllerState["X3"]`, `ControllerState["Y3"]`, `ControllerState["B1"]`, and `ControllerState["B2"]`, respectively. The other inputs are the hit boxes, time delay, and a predicate flag for whether the player is dead.
The `character` function logic is as follows:
<ul>
<li>Has the character hit an enemy?</li>
<ul><li> yes -> you're dead</li> <li>no -> you're not dead</li>
</ul>
<li>If not dead, then</li>
<ul>
<li>Check the jump button</li>
<li>Check the whip button</li>
</ul>
<li>Add gravity effect (you fall regardless of whether you're alive)</li>
<li>if not dead, </li>
<ul>
<li>if jumping, </li>
<ul>
<li>are you whipping?</li>
<ul>
<li>yes</li>
<li>no</li>
</ul>
</ul>
<li>else you're walking</li>
<ul>
<li>are you whipping?</li>
<ul>
<li>yes</li>
<li>no -> update normal walking controls</li>
</ul>
</ul>
</ul>
<li>else you're dead -> update death animation
<li>output Inset primitive with updated position and sprite image</li>
</ul>
</ul>
For example, while on the ground and not whipping, I use a [`Which`][28] expression to decide the update:
Which[
(* walking to the right *)
csX==1 && (csY==0 || csY==1),
moveRight[csX, fps];
playerCrouched=False;
(* animate character sprite *)
playerAnimationCounter += 1;
If[Mod[playerAnimationCounter,walkAnimationDelay]==0,
playerAnimationCounter=1;
im=walkingRight[[If[playerFrame==4,playerFrame=1,++playerFrame]]]
],
(* walking to the left *)
csX==-1 && (csY==0 || csY==1),
moveLeft[csX, fps];
playerCrouched=False;
(* animate character sprite *)
playerAnimationCounter += 1;
If[Mod[playerAnimationCounter,walkAnimationDelay]==0,
playerAnimationCounter=1;
im=walkingLeft[[If[playerFrame==4,playerFrame=1,++playerFrame]]]
],
(* crouched to the right *)
csX==1 && csY==-1,
If[!playerFacingRight, playerFacingRight=True];
playerCrouched=True;
playerAnimationCounter=9;
im=crouchRight,
(* crouched to the left *)
csX==-1 && csY==-1,
If[playerFacingRight, playerFacingRight=False];
playerCrouched=True;
playerAnimationCounter=9;
im=crouchLeft,
(* crouch down *)
csX==0 && csY==-1,
If[!playerCrouched,
playerCrouched=True;
im = If[playerFacingRight, crouchRight, crouchLeft]
],
(* stand up *)
csX==0 && csY==1,
playerFrame=1;
If[playerCrouched,
playerCrouched=False;
im = If[playerFacingRight, walkingRight, walkingLeft][[playerFrame]]
]
]
You'll see that the animation of the character involves a delayed update. So not only am I slowing the apparent motion with the `fps` variable, I also slow the apparent animation by using a `playerAnimationCounter` with a fixed `walkAnimationDelay`.
I did include a double jump, but it is also somewhat irrelevant for the simplicity of the level.
(* check state of jump button *)
Switch[{previousJumpState,csB},
{False,True}, If[jumpCounter<2,jumpCounter+=1;playerYvel=(jumpVel/fps);previousJumpState=True],
{True,False}, previousJumpState=False;
];
## Enemy Controls ##
----------
I use a [`Module`][29] to mimic "static" variables for the enemies as well.
Attributes[createEnemy] = {HoldRest};
createEnemy[index_Integer, whipBox_, enemyBox_, tsf_, score_, enemyReady_] := Module[
{xPos, yPos, xVel, yVel, im, speedTrigger=False, dying=False, size,
enemyAnimationCounter=1, enemyFrame=1, enemyAnimationDelay=25},
enemyBox = {{0,0},{0,0}};
initializeEnemy[index, "ghoulRight"] := ( . . . )
initializeEnemy[index, "ghoulLeft"] := ( . . . )
]
Within this module I define a number of functions that share the scoped module variables. The main function is `initializeEnemy` that itself defines another function `enemy`. It seems complicated, but this type of scoping helps avoid memory leaks and treats each enemy as an independent object. The `enemy` function processes the current state of the enemy and returns an `Inset` graphics primitive.
Adding a new type of enemy is straightforward; create a new `initializeEnemy` code block with an overloaded patter e.g. `initializeEnemy[index, "medusaRight"]`.
The ghoul logic is much simpler:
<ul>
<li>is the enemy dead?</li>
<ul> <li>no</li>
<ul>
<li>update position</li>
<li>have you hit the whip box? yes -> set dead flag</li>
<li>have you left the screen? yes -> move off screen and set ready flag</li>
</ul>
<li>yes -> do death animation and reset</li>
</ul>
<li>output Inset primitive with updated position and sprite image</li>
</ul>
</ul>
For example, the movement logic is much simpler than the character. It only moves left or right.
initializeEnemy[index, "ghoulLeft"] := (
. . .
enemy[index] := (
If[!dying,
(* if alive *)
(* update position and enemy hit box *)
xPos += xVel/tsf;
enemyBox = {{-0.037,-0.075}+{xPos,yPos},{0.029,0.081}+{xPos,yPos}};
. . .
]
)
. . .
)
## Hit Box Management ##
----------
The hit boxes follow the [`Rectangle`][30] syntax in that they specify a box by the lower-left and upper-right corners only. All hit boxes are un-rotated rectangles. I check whether the hit boxes are left/right or above/below of each other. If they are not, then they must be overlapping.
impactQ = Compile[{{box1,_Real,2},{box2,_Real,2}},
(* if one hitbox is on the left side of the other *)
If[box1[[2,1]] < box2[[1,1]] || box1[[1,1]] > box2[[2,1]], Return[False]];
(* if one hitbox is above the other *)
If[box1[[2,2]] < box2[[1,2]] || box1[[1,2]] > box2[[2,2]], Return[False]];
True
];
The enemies and player's hit boxes are given as relative distances from their centers. For example,
(* define character hit boxes *)
whipStandLeftBox = {{-0.2`,0.02`},{-0.06`,0.07`}};
whipStandRightBox = {{0.06`,0.02`},{0.2`,0.07`}};
whipCrouchedLeftBox = {{-0.204`,-0.02`},{-0.052`,0.034`}};
whipCrouchedRightBox = {{0.052`,-0.02`},{0.204`,0.034`}};
crouchedBox = {{-0.02`,-0.06`},{0.02`,0.038`}};
standingBox = {{-0.02`,-0.06`},{0.02`,0.086`}};
playerBox = standingBox + {{playerX,playerY},{playerX,playerY}};
I determined the hit boxes using a Manipulate:
Manipulate[
playerX = 0.288; playerY = 0.282; im = ImageReflect[Import["whip3.png"], Right];
Graphics[
{Inset[im, {playerX, playerY}, Scaled[{0.5, 0.5}], 0.562],
FaceForm[None], EdgeForm[Directive[Red, Thick]],
Dynamic[ Rectangle[{playerX + left, playerY + bottom}, {playerX + right, playerY + top}] ]
},
Frame -> False, ImageSize -> Medium, PlotRange -> {{0, 1}, {0, 0.5}}
]
,
{{left, -0.2}, -0.5, 0.5, LabeledSlider},
{{bottom, -0.2}, -0.5, 0.5, LabeledSlider},
{{right, 0.2}, -0.5, 0.5, LabeledSlider},
{{top, 0.2}, -0.5, 0.5, LabeledSlider}
]
![enter image description here][31]
The whip is more complicated. The whip's hit box is only active when the whip is fully extended, and its position depends on which direction the player is facing. For example, while in the air movement is allowed. Note in the following that `whipframe` #3 is when the whip box is active. Otherwise, within the `standWhipUpdate` function the whip box is moved off screen so it doesn't hit anything.
(* while in the air *)
playerFrame=1; playerCrouched=False;
Which[
csX==1, moveRight[csX, fps],
csX==-1, moveLeft[csX, fps]
];
If[whipping,
(* if whipping, player is in standing position *)
standWhipUpdate[whipBox];
(* whipping in the air is unique as movement is allowed; hit boxes need to update with the player's movement *)
playerBox = standingBox;
If[whipFrame==3, whipBox = If[playerFacingRight, whipStandRightBox, whipStandLeftBox] + {{playerX,playerY},{playerX,playerY}}],
(* if not whipping, the legs tuck midway through the jump *)
If[-(jumpVel/fps)/1.5 < playerYvel < (jumpVel/fps)/1.5,
playerBox = crouchedBox;
im = If[playerFacingRight, crouchRight, crouchLeft],
playerBox = standingBox;
im = If[playerFacingRight, walkingRight, walkingLeft][[playerFrame]];
];
]
The hit boxes are passed around between the functions. The player only cares about the enemy hit boxes:
If[
AnyTrue[{enemyBox[1],enemyBox[2],enemyBox[3],enemyBox[4],enemyBox[5],
enemyBox[6],enemyBox[7],enemyBox[8],enemyBox[9],enemyBox[10]},
impactQ[playerBox,#]&
],
playerDead = True; playerCrouched=True;
playerBox = {{1,1},{1,1}};
playerFrame=1;
im = If[playerFacingRight, playerDeathRight, playerDeathLeft][[playerFrame]];
playerAnimationCounter=1;
];
While the enemies only care about the whip hit box:
If[impactQ[whipBox,enemyBox],
dying = True; EmitSound[hitSound]; score++;
enemyBox = {{0,0},{0,0}};
size = 0.035;
enemyFrame=1;
im = enemyDeath[[enemyFrame]];
enemyAnimationCounter=1;
];
## Final bells and whistles ##
----------
The final steps include putting all the code into a larger [`DynamicModule`][32]. I added a static pause screen that also displays the controls as a [`PaneSelector`][33]. You can pause the game theoretically at any time, however the other dynamic updates can sometimes block the button. It works better by pressing and holding it until the screen appears, or by holding a direction on the directional pad and then press "start".
The "restart" button, that is only active after you die, has the same issue. I haven't figured out how to fix this.
When you restart after dying, you start in a crouched whipping position and your score is reset.
I also added sound effects for the whip and when you hit an enemy. I added the option for background music as an available MIDI file from the ([Video Game Music Headquarters][34]), since the song "Vampire Killer" is so iconic, but I did not loop it.
The enemies are generated based on a [`RandomVariate`][35] taken from a [`NormalDistribution`][36] with mean 100 'cycles' and a wide variance.
playLevel[] := Deploy@DynamicModule[{whipBox={{-10,-10},{-5,-5}}, fps=50,
enemyBox, playerDead=False, score=0, enemyReady, previousButtonState=False, pressed=False, enemyCounter=1,
enemyVariate=RandomVariate[NormalDistribution[100,30]]},
(* start background music *)
(*EmitSound[music];*)
(* use PaneSelector to toggle between pause screen and active game *)
PaneSelector[
{
True ->
Column[{
Graphics[
{
Inset[background, {0,0}, Scaled[{0,0}], 4.19],
Dynamic[Inset[Style[score,Red,Bold,27], {scrollPos+screenWidth-0.1,0.9}]],
Dynamic[
If[playerDead,
If[ControllerState["B7"],
playerDead=False; score=0; character[{1,-1,False,True}, enemyBox, whipBox, fps, playerDead]
];
Inset[Panel[Style[" Play again? \nPress Restart Button.",Red,Bold,27],Background->Black], {scrollPos+screenWidth*0.5,0.5}],
{}
]
],
Dynamic[{
If[(enemyCounter += 1) > enemyVariate,
enemyCounter=1; enemyVariate=RandomVariate[NormalDistribution[100,30]];
initializeEnemy[
First[Pick[{1,2,3,4,5,6,7,8,9,10}, enemyReady /@ {1,2,3,4,5,6,7,8,9,10}], {}],
RandomChoice[{"ghoulLeft","ghoulRight"}]
]
];
enemy[1],enemy[2],enemy[3],enemy[4],enemy[5],
enemy[6],enemy[7],enemy[8],enemy[9],enemy[10],
character[
{
ControllerState["X3"],ControllerState["Y3"],
ControllerState["B1"],ControllerState["B3"]
},
enemyBox, whipBox, fps, playerDead
]
}]
},
Frame -> False,
ImageSize -> Large,
PlotRange -> {{Dynamic[0+scrollPos],Dynamic[screenWidth+scrollPos, TrackedSymbols:>{scrollPos}]}, {0,1}},
PlotRangePadding -> None,
PlotRangeClipping -> True,
ImagePadding -> 1
](*,
LabeledSlider[Dynamic[fps],{1,72}]*)
}],
False -> controls
},
Dynamic[
Switch[{previousButtonState,ControllerState["B8"]},
{False,True}, If[pressed,pressed=False,pressed=True]; previousButtonState=True,
{True,False}, previousButtonState=False
];
pressed
],
ImageSize -> Automatic
](* end PaneSelector *),
Initialization :> (
Do[With[{i=i}, createEnemy[i, whipBox, enemyBox[i], fps, score, enemyReady]], {i,1,10}];
enemy[1]=enemy[2]=enemy[3]=enemy[4]=enemy[5]=enemy[6]=enemy[7]=enemy[8]=enemy[9]=enemy[10] = {};
enemyReady[1]=enemyReady[2]=enemyReady[3]=enemyReady[4]=enemyReady[5] = True;
enemyReady[6]=enemyReady[7]=enemyReady[8]=enemyReady[9]=enemyReady[10] = True;
)
]
[1]: http://community.wolfram.com/groups/-/m/t/1127985
[2]: http://community.wolfram.com//c/portal/getImageAttachment?filename=play.gif&userId=829295
[3]: https://github.com/KMDaily/CastlevaniaDemo_WolframLanguage
[4]: http://reference.wolfram.com/language/ref/ControllerState.html
[5]: http://community.wolfram.com//c/portal/getImageAttachment?filename=Controls2.png&userId=829295
[6]: http://reference.wolfram.com/language/ref/PlotRange.html
[7]: http://reference.wolfram.com/language/ref/Insert.html
[8]: http://reference.wolfram.com/language/ref/Graphics.html
[9]: http://community.wolfram.com//c/portal/getImageAttachment?filename=LevelMap.png&userId=829295
[10]: http://reference.wolfram.com/language/ref/PlotRange.html
[11]: http://community.wolfram.com//c/portal/getImageAttachment?filename=scrollPosition.PNG&userId=829295
[12]: http://reference.wolfram.com/language/ref/ScheduledTask.html
[13]: http://reference.wolfram.com/language/ref/Dynamic.html
[14]: http://community.wolfram.com//c/portal/getImageAttachment?filename=slowDown.PNG&userId=829295
[15]: https://www.spriters-resource.com/
[16]: http://community.wolfram.com//c/portal/getImageAttachment?filename=characterWalking.PNG&userId=829295
[17]: http://community.wolfram.com//c/portal/getImageAttachment?filename=6947characterSpriteExtent.PNG&userId=829295
[18]: http://community.wolfram.com//c/portal/getImageAttachment?filename=characterCrouchExtent.PNG&userId=829295
[19]: http://community.wolfram.com//c/portal/getImageAttachment?filename=characterDeath.PNG&userId=829295
[20]: http://reference.wolfram.com/language/ref/RemoveBackground.html
[21]: http://reference.wolfram.com/language/ref/ImageCrop.html
[22]: http://reference.wolfram.com/language/ref/Inset.html
[23]: http://reference.wolfram.com/language/ref/ImagePad.html
[24]: http://reference.wolfram.com/language/ref/Manipulate.html
[25]: http://community.wolfram.com//c/portal/getImageAttachment?filename=imageAlignment.PNG&userId=829295
[26]: http://community.wolfram.com//c/portal/getImageAttachment?filename=3893reflectionAlignment.PNG&userId=829295
[27]: http://reference.wolfram.com/language/ref/Module.html
[28]: http://reference.wolfram.com/language/ref/Which.html
[29]: http://reference.wolfram.com/language/ref/Module.html
[30]: http://reference.wolfram.com/language/ref/Rectangle.html
[31]: http://community.wolfram.com//c/portal/getImageAttachment?filename=whipbox.PNG&userId=829295
[32]: http://reference.wolfram.com/language/ref/DynamicModule.html
[33]: http://reference.wolfram.com/language/ref/PaneSelector.html
[34]: http://www.vgmusic.com
[35]: http://reference.wolfram.com/language/ref/RandomVariate.html
[36]: http://reference.wolfram.com/language/ref/NormalDistribution.htmlKevin Daily2017-08-09T21:18:36Z[WSC17] Visualizing shapes inside Moire patterns using Mathematica
http://community.wolfram.com/groups/-/m/t/1161905
Greetings,
The focus of this post is going to be how we can use the Wolfram language and Mathematica to better visualize the shapes present in a given Moire pattern. First, we will briefly review what Moire patterns are, and how to build one in Mathematica. Then, we will look at the different functions that allow us to see the shapes inside Moire patterns better. Lastly, we will use these functions to create interesting Manipulate and Cloud objects.
Introduction to Moire patterns
-------------------------------------------
The basic definition of a Moire pattern is: A pattern that can be observed when a ruled pattern with is overlaid with another rotated/displaced version of the same (or similar) pattern. They are not only mesmerizing to watch, but have real life uses as well. For example in Physics, where a pattern is projected on a surface. The surface is then modified, and so the projected pattern changes. Both, the new and the original pattern are overlaid. Scientists can look at the resulting Moire pattern to determine the change in the surface. Here is a an example of a Moire Pattern:
![Moire Pattern built using Mathematica][1]
This example, built with Mathematica, shows a set of equidistant points, overlaid by the same set, rotated 70 Degrees.
The code to do so was:
pts = Table[Point[{x, y}], {x, -7, 7}, {y, -7, 7}];
Graphics[{pts, Rotate[pts, 70 Degree]}]
*First, we generate the set of points (pts), and then we rotate it using the function Rotate.*
In Mathematica you can not only generate moire patterns based on sets of points, but you can do so with other shapes as well.
Let's take lines for example:
lines = Table[Line[{{x, 0}, {x, 1}}], {x, 0, 1, 0.05}];
Graphics@Table[Rotate[lines, r], {r, \[Pi]/5, \[Pi], \[Pi]/5}]
The resulting Moire Pattern would be:
![Linear Moire created using Mathematica][2]
In this particular example, we generated a table of sets of lines, instead of just using 2. There is no special reason to it, it's just so it looks better. You can do it with just two sets aswell:
lines = Table[Line[{{x, 0}, {x, 1}}], {x, 0, 1, 0.05}];
Graphics[{lines, Rotate[lines, 20 Degree]}]
![Linear Moire created using Mathematica][3]
You can also play around with Manipulate and Moire patterns. A cool example would be:
points = Table[Point[{x, y}], {x, -10, 10}, {y, -10, 10}];
Manipulate[Graphics[Table[Rotate[Style[points, Hue[a]], a], {a, a, 4 a, \[Pi]/16}]], {a, 0, \[Pi]/4}]
*Beware: This Manipulate may require quite a bit of computing*
The result should look something like this:
![Moire "Flower" generated in Mathematica][4]
Making shapes in Moire patterns more visible
--------------------------------------------
Mathematica has an array of different functions used to analyze images and graphics. I explored a few of them and found out that the best functions to visualize the shapes inside Moire patterns were SkeletonTransform and VoronoiMesh. Also, I looked at Histogram3D to see how the density of the points in a Moire pattern changed over time.
Let's start with the function Skeleton. Before using it, we must convert the graph into an image, because Skeleton only takes images as Arguments. To do so, we just use Rasterize
graph=With[{points = Table[Point[{x, y}], {x, -15, 15}, {y, -15, 15}]}, Graphics[{points, Rotate[points, 70]}]];
img=Rasterize[graph]
*Note: Instead of defining pts to do the graph, we can use the function With, as used in the last code*
Now that we have an image, we can use SkeletonTransform to get the shapes. We will also crop the image so it's easier to see the shapes.
SkeletonTransform[ImageCrop[img, Round[ImageDimensions[img]/2]]] // ImageAdjust
The output should look like this:
![Skeleton of a Moire pattern][5]
SkeletonTransform does a good enough job representing the shapes, but there is another function that represents them better/clearer. It is called VoronoiMesh and it generates a Mesh, consisting of cells around the points it is given. To use this function, we need to use another Method of rotating the points. So, instead of using Rotate as we did before, we will use RotationTransform to get the coordinates of the rotated points. We will crop the image as we did with SkeletonTransform, just so the shapes are clearer.
points = Table[Point[{x, y}], {x, -10, 10}, {y, -10, 10}];
r = RotationTransform[\[Pi]/4];
rotatedpoints = points /. Point -> r;
finalpoints = rotatedpoints /. {x_, y_} -> Point[{x, y}];
setofpoints = Join[points, finalpoints];
densitypoints = setofpoints /. Point[{x_, y_}] -> {x, y};
flat = Flatten[densitypoints, 1];
i = VoronoiMesh[flat, PlotTheme -> "Lines"];
image = Rasterize[i];
ImageCrop[image, Round[ImageDimensions[image]/2]] // ImageAdjust
The output should look like this:
![Voronoi Mesh][6]
*Note: Try different angles to get different shapes. (The angle in this code is determined by the argument of RotationTransform)*
I talked about how we could use Histogram3D to see how the density of the points changed with each angle. There are two ways to approach this. You can make a function and manually change the angle to get each Histogram3D for each given angle, or you could do a Manipulate to slide and see how it changes. I took the second approach and added a graph of the Moire pattern in itself to better visualize the change.
Here is the code for the Manipulate:
Manipulate[
Module[
{MoireHistogram, graph},
MoireHistogram[a_] :=
Module[{points, r, rotatedpoints, finalpoints, setofpoints,
densitypoints, flat2},
points = Table[Point[{x, y}], {x, -d, d}, {y, -d, d}];
r := RotationTransform[a];
rotatedpoints := points /. Point -> r;
finalpoints := rotatedpoints /. {x_, y_} -> Point[{x, y}];
setofpoints := Join[points, finalpoints];
densitypoints := setofpoints /. Point[{x_, y_}] -> {x, y};
flat2 := Flatten[densitypoints, 1];
If[gd,
Histogram3D[flat2, Automatic,
ChartElementFunction -> "GradientScaleCube"],
Histogram3D[flat2]]
];
graph[a_] :=
With[{points = Table[Point[{x, y}], {x, -d, d}, {y, -d, d}]},
Graphics[{points, Rotate[points, a]}]];
{Magnify[graph[a], 1.5], Magnify[MoireHistogram[a], 2]}],
{{a, \[Pi]/4, "Angle"}, 0, \[Pi]/2},
{{d, 4, "Density"}, 2, 15, 1},
{{gd, False, "Gradient Scale Cubes"}, {True, False}}
]
And here is a snapshot of the end product:
![Snapshot of a Moire Pattern and its' 3D Histogram][7]
Interesting Manipulate and Cloud objects
----------------------------------------
We have already seen some Manipulate objects, but there is more to it. For example, with Mathematica, you can create a Manipulate that lets you change the angle, point density and color of a VoroniMesh from a moire pattern, all in the same Manipulate:
Manipulate[
{Module[
{points, r, rotatedpoints, finalpoints, setofpoints, densitypoints,
flat},
points = Table[Point[{x, y}], {x, -d, d}, {y, -d, d}];
r = RotationTransform[a];
rotatedpoints = points /. Point -> r;
finalpoints = rotatedpoints /. {x_, y_} -> Point[{x, y}];
setofpoints = Join[points, finalpoints];
densitypoints = setofpoints /. Point[{x_, y_}] -> {x, y};
flat = Flatten[densitypoints, 1];
If[showpoints,
i = Graphics[{co,
MeshPrimitives[VoronoiMesh[flat, PlotTheme -> "Lines"], 1],
co2, Point[flat]}],
i = Graphics[{co,
MeshPrimitives[VoronoiMesh[flat, PlotTheme -> "Lines"], 1]}]];
image = ImageResize[Rasterize[i, ImageResolution -> 100], "Large"];
Magnify[ImageCrop[image, Round[ImageDimensions[image]/2]], 3]
]},
{{a, \[Pi]/4, "Angle"}, 0, 1.569},
{{d, 4, "Density"}, 2, 17, 1},
{{showpoints, False, "Show Voronoi Points"}, {True, False}},
{{co, Black, "Line Color"}, Black},
{{co2, Orange, "Point Color"}, Orange}
]
And the corresponding snapshot:
![Voroni Manipulate object from Mathematica][8]
Moving on to the Cloud objects, we can create a Microsite that receives an angle and a point density, to return a Moire pattern and its Voronoi Mesh. To do so we need to first define a function, then a FormFucntion and lastly CloudDeploy that FormFunction.
func1[a_, d_] :=
Module[{MoireVoD, graphD},
MoireVoD[a, d] :=
Module[{points, r, rotatedpoints, finalpoints, setofpoints,
densitypoints, flat},
points = Table[Point[{x, y}], {x, -d, d}, {y, -d, d}];
r = RotationTransform[a];
rotatedpoints = points /. Point -> r;
finalpoints = rotatedpoints /. {x_, y_} -> Point[{x, y}];
setofpoints = Join[points, finalpoints];
densitypoints = setofpoints /. Point[{x_, y_}] -> {x, y};
flat = Flatten[densitypoints, 1];
i = VoronoiMesh[flat, PlotTheme -> "Lines"];
image = Rasterize[i];
ImageCrop[image, Round[ImageDimensions[image]/2]] // ImageAdjust
];
graphD[a, d] :=
With[{points = Table[Point[{x, y}], {x, -d, d}, {y, -d, d}]},
Graphics[{points, Rotate[points, a]}]];
{graphD[a, d], MoireVoD[a, d]}]
form1 = FormFunction[{"a" -> "Real", "d" -> "Integer"},
func1[#a, #d] &,
AppearanceRules -> <|"Title" -> "Moire and Voronoi",
"Description" ->
"Enter an Angle (a) between 0 and 1.569 (Radians) and a point \
density (d) between 4 and 17 to receive a Moire Pattern with its' \
corresponding Voronoi Mesh"|>]
CloudDeploy[form1, "Moire and Voronoi", Permissions -> "Public"]
You can click [here ][9] to go to the Microsite.
Conclusions
-----------
We have seen how Mathematica has different ways to display the same data, in this case, how it can display the information given by a Morie pattern. I am sure that I missed a couple of functions, so, if you have any suggestions for which other functions might represent Moire patterns better, feel free to discuss them in the reply section. Also, if you think anything in this post could have been done better, do not hesitate and reply. All feedback is greatly appreciated.
[1]: http://community.wolfram.com//c/portal/getImageAttachment?filename=8508Moire.png&userId=1138553
[2]: http://community.wolfram.com//c/portal/getImageAttachment?filename=LinearMoires.png&userId=1138553
[3]: http://community.wolfram.com//c/portal/getImageAttachment?filename=LinearMoires2.png&userId=1138553
[4]: http://community.wolfram.com//c/portal/getImageAttachment?filename=MoireMan.png&userId=1138553
[5]: http://community.wolfram.com//c/portal/getImageAttachment?filename=Skeletonsmol.png&userId=1138553
[6]: http://community.wolfram.com//c/portal/getImageAttachment?filename=Voronoi1.png&userId=1138553
[7]: http://community.wolfram.com//c/portal/getImageAttachment?filename=ManipHisto.PNG&userId=1138553
[8]: http://community.wolfram.com//c/portal/getImageAttachment?filename=MoireVor.PNG&userId=1138553
[9]: https://www.wolframcloud.com/objects/user-6b332570-e883-46c3-9c26-d2277ce655ad/Moire%20and%20Voronoi%20Alpha/%22Here%22Oleg Rouvinski2017-08-10T18:17:07Z