The Magical Cloud

For our final project, our team (Vitoria and Aya), decided to explore the idea of an artificial cloud that worked as a sort of weather gadget. By searching for the weather, you would get a visual representation of what the weather was.

Initial sketch for idea:

From the very beginning, it was clear that obtaining in real time weather updates that would stimulate several functions and actions would be very difficult and perhaps too time consuming to try and figure out. We decided early on that we would choose certain cities to program their weather into the code manually. This also led to a design choice for the digital aspect of the piece. We decided to create a map on screen where you could press on a city to trigger the response from the physical cloud.

We first started by designing and printing rain and snow shapes out of acrylic using a laser cutter. We tested the motion of the rain by attaching them to servos and coding their movements. Originally the shapes were attached to long sticks that were attached to the servos. However, we did not like how the movements were jerky instead of smooth. Also, the long sticks interfered with a lot of the design decisions and made the building process of the cloud more difficult. We then decided to create a yo-yo based system that would be attached to the servo and the shape and roll the shape up and down.

After managing the servos, we moved on to looking at the lights we wanted to use. We started with RGB LEDs, however, we realized that it was pointless to try and program a large number of LEDs. Instead, we decided to use neopixel strips based off of Aaron’s advice to us. This was easier in terms of coding, power, and design.

Then came the building process, which was being played with the whole time. We used a clothing rack as the foundational support. We then added shelves to it to support the Arduino board as well as the servos. The neopixel strips were draped over the steel rods. We used lanterns as the base for the cloud and used white glue to stick cotton on to them. They worked well because they already had the required structure so that the design wouldn’t interfere with the servos and lights.

After building the cloud and working the physical parts, we began to work on the digital aspect. This went relatively smoothly using processing. This definetly took the least time.

Hooking up processing to Arduino created a few challenges. Trying to run everything at once created a lot of lagging. However, we added an external power source and that helped.

Lastly, we added on-screen feedback and audio (because of the feedback we received in class).

Disclaimer: The coding process involved a lot of help from people around the lab, Aaron, and the internet.

Processing Code: 

import java.awt.Rectangle;
import ddf.minim.*;
import processing.serial.*;
Serial myPort; // Create object from Serial class

Minim minim;
AudioPlayer rain;
AudioPlayer lightrain;
AudioPlayer heavyrain;
AudioPlayer storm;
AudioPlayer snow;
AudioPlayer birds;

PImage img;
PFont font;
boolean newYorkPressed = false;
boolean shanghaiPressed = false;
boolean abuDhabiPressed = false;
boolean tokyoPressed = false;
boolean parisPressed = false;
boolean florencePressed = false;
boolean madridPressed = false;
boolean baPressed = false;
boolean havanaPressed = false;
boolean icePressed = false;
boolean capeTownPressed = false;
boolean rainSound = false;
boolean birdSound = false;
boolean heavyRain = false;
boolean lightRain = false;
boolean stormSound = false;
boolean snowySound = false;

Rectangle newyork;
Rectangle shanghai;
Rectangle abudhabi;
Rectangle tokyo;
Rectangle paris;
Rectangle florence;
Rectangle madrid;
Rectangle ba;
Rectangle havana;
Rectangle ice;
Rectangle capetown;

void setup() {
String portName = Serial.list()[3]; //change the 0 to a 1 or 2 etc. to match your port
myPort = new Serial(this, portName, 9600);
fullScreen();
img = loadImage(“hey.jpeg”);
font = loadFont(“Futura-CondensedExtraBold-48.vlw”);
newyork= new Rectangle(371, 313, 160, 50);
shanghai = new Rectangle(1090, 370, 160, 40);
abudhabi = new Rectangle(890, 412, 175, 50);
tokyo =new Rectangle(1232, 344, 110, 40);
paris = new Rectangle(695, 255, 110, 40);
florence = new Rectangle(725, 320, 130, 50);
madrid = new Rectangle(540, 308, 110, 45);
ba = new Rectangle(420, 723, 215, 50);
havana = new Rectangle(360, 420, 160, 40);
ice = new Rectangle(593, 153, 150, 50);
capetown = new Rectangle(755, 730, 290, 50);
minim = new Minim(this);
birds = minim.loadFile(“birds.mp3”);
rain = minim.loadFile(“rain.mp3”);
lightrain = minim.loadFile(“lightrain.mp3”);
heavyrain = minim.loadFile(“rain2.mp3”);
storm =minim.loadFile(“storm.mp3”);
snow = minim.loadFile(“snow.mp3”);
}

void draw() {
//image(img, 0, 0, width, height);
img.resize(width, height);
background(img);
textFont(font, 32);
fill(#50F2FF);
text(“New York”, 370, 340); //437,330
text(“Shanghai”, 1100, 400); //1167,391
text(“Abu Dhabi”, 895, 440); // 968,429
text(“Tokyo”, 1240, 375); //1280,362
text(“Paris”, 698, 286); //735,276
text(“Florence”, 732, 345); // 791,335
text(“Madrid”, 550, 335); // 603,325
text(“Buenos Aires”, 427, 750); //523,742
text(“Havana”, 365, 446); // 420, 438
text(“Reykjavik”, 600, 190); // 666,181
text(“Cape Town”, 765, 760); // 840,751

textFont(font, 45);
fill(0);

if (newYorkPressed==true && birdSound == true) {
text(“New York is currently: Sunny”, 450, 850);
pause();
birds.play();
}
if (shanghaiPressed == true && rainSound ==true) {
text(“Shanghai is currently: Rainy “, 450, 850);
pause();
rain.play();
}

if ( abuDhabiPressed == true && birdSound == true) {
text(“Abu Dhabi is currently: Sunny “, 450, 850);
pause();
birds.play();
}
if (tokyoPressed==true && heavyRain == true)
{
text(“Tokyo is experiencing: Heavy Rain Fall “, 450, 850);
pause();
heavyrain.play();
}
if (parisPressed == true&& birdSound == true) {

text(“Paris is currently: Sunny”, 450, 850);
pause();
birds.play();
}

if ( florencePressed == true && lightRain == true) {
text(“Florence is experiencing: Sunshine and Rain “, 450, 850);
pause();
lightrain.play();
}

if (madridPressed==true && birdSound == true)
{
text(“Madrid is currently: Sunny “, 450, 850);
pause();
birds.play();
}
if (baPressed == true && stormSound == true) {

text(“Buenos Aires is experiencing: Storms “, 450, 850);
pause();
storm.play();
}

if (havanaPressed == true && lightRain == true) {
text(“Havana is experiencing: Sunshine and Rain “, 450, 850);
pause();
lightrain.play();
}
if (icePressed==true && snowySound == true)
{
text(“Reykjavik is currently: Snowing “, 450, 850);
pause();
snow.play();
}
if (capeTownPressed == true && birdSound == true) {
text(“Cape Town is currently: Sunny “, 450, 850);
pause();
birds.play();
}
}

void mousePressed() {
textFont(font, 32);
fill(#50F2FF);
if (newyork.contains(mouseX, mouseY)) {
myPort.write(3);
newYorkPressed = true;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = false;
florencePressed = false;
madridPressed = false;
baPressed = false;
havanaPressed = false;
icePressed = false;
capeTownPressed = false;

rainSound = false;
birdSound = true;
heavyRain = false;
lightRain = false;
stormSound = false;
snowySound = false;
}

if (shanghai.contains(mouseX, mouseY)) {
myPort.write(0);
newYorkPressed = false;
shanghaiPressed = true;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = false;
florencePressed = false;
madridPressed = false;
baPressed = false;
havanaPressed = false;
icePressed = false;
capeTownPressed = false;

rainSound = true;
birdSound = false;
heavyRain = false;
lightRain = false;
stormSound = false;
snowySound = false;
}

if (abudhabi.contains(mouseX, mouseY)) {
myPort.write(3);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = true;
tokyoPressed = false;
parisPressed = false;
florencePressed = false;
madridPressed = false;
baPressed = false;
havanaPressed = false;
icePressed = false;
capeTownPressed = false;

rainSound = false;
birdSound = true;
heavyRain = false;
lightRain = false;
stormSound = false;
snowySound = false;
}

if (tokyo.contains(mouseX, mouseY)) {
myPort.write(1);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = true;
parisPressed = false;
florencePressed = false;
madridPressed = false;
baPressed = false;
havanaPressed = false;
icePressed = false;
capeTownPressed = false;

rainSound = false;
birdSound = false;
heavyRain = true;
lightRain = false;
stormSound = false;
snowySound = false;
}

if (paris.contains(mouseX, mouseY)) {
myPort.write(3);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = true;
florencePressed = false;
madridPressed = false;
baPressed = false;
havanaPressed = false;
icePressed = false;
capeTownPressed = false;

rainSound = false;
birdSound = true;
heavyRain = false;
lightRain = false;
stormSound = false;
snowySound = false;
}
if (florence.contains(mouseX, mouseY)) {
myPort.write(5);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = false;
florencePressed = true;
madridPressed = false;
baPressed = false;
havanaPressed = false;
icePressed = false;
capeTownPressed = false;
rainSound = false;
birdSound = false;
heavyRain = false;
lightRain = true;
stormSound = false;
snowySound = false;
}
if (madrid.contains(mouseX, mouseY)) {
myPort.write(3);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = false;
florencePressed = false;
madridPressed = true;
baPressed = false;
havanaPressed = false;
icePressed = false;
capeTownPressed = false;

rainSound = false;
birdSound = true;
heavyRain = false;
lightRain = false;
stormSound = false;
snowySound = false;
}
if (ba.contains(mouseX, mouseY)) {
myPort.write(0);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = false;
florencePressed = false;
madridPressed = false;
baPressed = true;
havanaPressed = false;
icePressed = false;
capeTownPressed = false;
rainSound = false;
birdSound = false;
heavyRain = false;
lightRain = false;
stormSound = true;
snowySound = false;
}
if (havana.contains(mouseX, mouseY)) {
myPort.write(5);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = false;
florencePressed = false;
madridPressed = false;
baPressed = false;
havanaPressed = true;
icePressed = false;
capeTownPressed = false;
rainSound = false;
birdSound = false;
heavyRain = false;
lightRain = true;
stormSound = false;
snowySound = false;
}
if (ice.contains(mouseX, mouseY)) {
myPort.write(2);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = false;
florencePressed = false;
madridPressed = false;
baPressed = false;
havanaPressed = false;
icePressed = true;
capeTownPressed = false;
rainSound = false;
birdSound = false;
heavyRain = false;
lightRain = false;
stormSound = false;
snowySound = true;
;
}
if (capetown.contains(mouseX, mouseY)) {
myPort.write(3);
newYorkPressed = false;
shanghaiPressed = false;
abuDhabiPressed = false;
tokyoPressed = false;
parisPressed = false;
florencePressed = false;
madridPressed = false;
baPressed = false;
havanaPressed = false;
icePressed = false;
capeTownPressed = true;
rainSound = false;
birdSound = true;
heavyRain = false;
lightRain = false;
stormSound = false;
snowySound = false;
}
}

void pause() {
rain.pause();
lightrain.pause();
heavyrain.pause();
storm.pause();
snow.pause();
birds.pause();
}

Arduino Code: 

int mode = 3;

#include <Servo.h>

Servo myservo;
Servo myservo2;

int pos = 0;

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 11
#define NUMPIXELS 70

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
Serial.begin(9600);
myservo.attach(9);
myservo2.attach(3);
pixels.begin();

}

void flash(){
for(int i=0;i<NUMPIXELS;i++){
pixels.setPixelColor(i, pixels.Color(2,140,242));

pixels.show();

delay(100);
}
for (pos = 0; pos <= 180; pos += 1) {
// in steps of 1 degree
myservo.write(pos);
delay(20);
}
for (pos = 180; pos >= 0; pos -= 1) {
myservo.write(pos);
delay(20);
}

// for(int i=0;i<NUMPIXELS;i++){
// pixels.setPixelColor(i, pixels.Color(2,140,242));
//
// pixels.show();
// }
//
// for (pos = 0; pos <= 180; pos += 1) {
// // in steps of 1 degree
// myservo.write(pos);
// delay(15);
// }
// for (pos = 180; pos >= 0; pos -= 1) {
// myservo.write(pos);
// delay(15);
// }
}

void heavyrain(){

for (pos = 0; pos <= 180; pos += 1) {
// in steps of 1 degree
myservo.write(pos);
delay(15);
}
for (pos = 180; pos >= 0; pos -= 1) {
myservo.write(pos);
delay(15);
}

}
// light flicker in
void light(){

for(int i=0;i<NUMPIXELS;i++){
pixels.setPixelColor(i, pixels.Color(255,255,255));

pixels.show();

delay(500);

}
}
// motor spin at slow speed
void rain(){
for(int i=0;i<NUMPIXELS;i++){
pixels.setPixelColor(i, pixels.Color(2,140,242));

pixels.show();

delay(100);
}
for (pos = 0; pos <= 180; pos += 1) {
// in steps of 1 degree
myservo.write(pos);
delay(20);
}
for (pos = 180; pos >= 0; pos -= 1) {
myservo.write(pos);
delay(20);
}
}
// motors spin at medium speed
void snow(){
for(int i=0;i<NUMPIXELS;i++){
pixels.setPixelColor(i, pixels.Color(172,217,250));

pixels.show();

delay(100);
for (pos = 0; pos <= 180; pos += 1) {
// in steps of 1 degree
myservo2.write(pos);
delay(15);
}
for (pos = 180; pos >= 0; pos -= 1) {
myservo2.write(pos);
delay(15);
}
}
}
void sunrain(){

for(int i=0;i<NUMPIXELS;i++){
pixels.setPixelColor(i, pixels.Color(152,211,255));

pixels.show();

delay(100);

for (pos = 0; pos <= 180; pos += 1) {
// in steps of 1 degree
myservo.write(pos);
delay(20);
}
for (pos = 180; pos >= 0; pos -= 1) {
myservo.write(pos);
delay(20);
}
}
}
void stopLights() {
for (int i = 0; i < NUMPIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
}
pixels.show();
}

void sun(){

for(int i=0;i<NUMPIXELS;i++){
pixels.setPixelColor(i, pixels.Color(255,255,255));

pixels.show();

delay(100);

}
}

void loop() {
// put your main code here, to run repeatedly:

if (Serial.available())
{ // If data is available to read,
mode = Serial.read(); // read it and store it in val
}

switch (mode) {
case 0:
stopLights();
myservo.detach();
myservo2.detach();
myservo.attach(9);
rain();
break;
case 1:
stopLights();
myservo.detach();
myservo2.detach();
myservo.attach(9);
heavyrain();
break;
case 2:
stopLights();
myservo.detach();
myservo2.detach();
myservo2.attach(3);
snow();
break;
case 3:
myservo.detach();
myservo2.detach();
sun();
break;
case 4:
myservo.detach();
myservo2.detach();
flash();
break;
case 5:
myservo.detach();
myservo2.detach();
myservo.attach(9);
sunrain();
}

}

Final Product:

 

The entire process was a lot fo trial and error, there were a lot of issues that remained unsolved, however we received really great feedback at the showcase and we are still happy to end the course with this project.

Computer Vision for Artists and Designers: Response

This reading really protrays how drastically computer-related research and functions have changed. Going from an exclusive feature to an accessible, easily transferable function, the use of computer vision has dramatically changed.

An interesting point in the reading was about the role of the entire human body in the interaction with a computer. I found Videoplace an incredibly interesting launchpad for human-computer interaction because of its obvious and still-apparent affects in the field and in other interactive art. The development from simply human movement to also the incorporation of other senses, such as sight and speech, was also interesting, because it shows the endless potential of interactive pieces according to a designers needs and motives. Moving forward, the potential for interactive instillation art is also explored- perhaps the most interesting part to me. I really enjoyed this because instillation art is meant to engage an audience, and combing it with this field allows obvious interaction, which is uplifting to audience engagement.

The reading also touched on the possible problems one may encounter when attempting to create interactive work. This especially resonated with me because often times, I will scratch an idea because I don’t know how and if I would be able to do it. Detecting these problems and finding solutions enables artists and designers to pursue projects regardless of the possible failures that may result from the technical bits.

All in all, the computer function of design opens up endless possibilities for artists and their works- there is no rejection of any crazy idea.

Assignment Week #11

For this week’s project, we needed to find a way to combine both Arduino and Processing. I ended up basing the code off of the art project that we did a few weeks ago. I tried to think of some ways to use different things other than the knobs such as Aaron suggested, and ended up using two controllers: a knob and a pressure sensor.

I just thought it would be interesting to work with both these sensors in relation to one another and see the power of each in relation to affecting the processing drawing.

Codes:

Arduino:

void setup() {
Serial.begin(9600);
}

void loop() {
if(Serial.available()>0){
char inByte=Serial.read();
int sensor = analogRead(A0);
delay(0);
int sensor2 = analogRead(A1);
delay(0);
Serial.print(sensor);
Serial.print(‘,’);
Serial.println(sensor2);
}
}

Processing:

import processing.serial.*;
Serial myPort;
int xPos=0;
int yPos=0;

Shape shape1;
Shape shape2;
Shape shape3;
Shape shape4;
Shape shape5;
Shape shape6;
Shape shape7;
Shape shape8;

int x1= mouseX;
int y1 = mouseY;
color red = color(255, 0, 0, 20);
color green = color(0, 255, 0, 30);
color blue = color(0, 0, 255, 10);

void setup(){
size(960,720);
printArray(Serial.list());
String portname=Serial.list()[3];
println(portname);
myPort = new Serial(this,portname,9600);
myPort.clear();
myPort.bufferUntil(‘\n’);
smooth();
colorMode(HSB);
shape1 = new Shape(xPos, yPos, 250, 150, 250, 250, 100, 100);
shape2 = new Shape(xPos, yPos, 150, 250, 250, 250, 400, 200);
shape3 = new Shape(xPos, yPos, 400, 250, 250, 250, 100, 400);
shape4 = new Shape(xPos, yPos, 250, 400, 250, 250, 50, 290);

}
void draw(){
background(100, 105, 100);
shape1.display();
shape2.display();
shape3.display();
shape4.display();
noStroke();
}
void serialEvent(Serial myPort){
String s=myPort.readStringUntil(‘\n’);
s=trim(s);
if (s!=null){
int values[]=int(split(s,’,’));
if (values.length==2){
xPos=(int)map(values[0],0,1023,0, width);
yPos=(int)map(values[1],0,1023,0, height);
}
}
println(xPos);
myPort.write(‘0’);
}

Class Processing:

class Shape{

//Variables
int x1, y1, x2, y2, x3, y3, x4, y4;

//The Constuctor
Shape(int px1, int py1, int px2, int py2, int px3, int py3, int px4, int py4){
x1 = px1;
y1 = py1;
x2 = px2;
y2 = py2;
x3 = px3;
y3 = py3;
x4 = px4;
y4 = py4;
}
//Functions

void display(){

x1 = xPos; //use xPos and yPos
y1 = yPos;
quad(x1, y1, x2, y2, x3, y3, x4, y4);

}

}

 

 

What Computing Means to Me

Honestly, before this class, I had always been aware of how important computing is, just never realized the nitty-bitty parts of it. The class made me appreciate all the things that really go into computing and all the things I interact with on a regular basis that I hadn’t really thought about before.

I think the most significant part for me was the intersection with the arts. It really interested me to see the possibilities of artistic creation through computing. For me, the way that the two forms intersected aligned with the type of work I find intriguing, as well as the type of work I could see myself exploring for my own artistic growth.

Digitize Everything: Response

I think that this reading was fairly simple and easy to understand. The examples he used to get across the ideas were very normal, simple examples such as the GPS and the photocopying. These simple examples make it easy to visualize the concepts he is trying to explain because the reader does not feel pressured to first try and understand the concept.

I really liked the Machine to Machine concept, where machines have developed their own network of communication that requires no human intervention.

Digitization has been useful and productive in the creation and spreading of information and knowledge. It has helped in everyday life tasks, as well as research and statistical analysis of lifestyle, money, interests, etc.

Art Project

I struggled a lot with just trying to figure out how to work the ‘class’ thin, even though I understood the overall concept. Eventually, after watching a few videos and getting help from classmates, I worked it out.

My code is as follows:

//Main CircleDrawing
Shape shape1;
Shape shape2;
Shape shape3;
Shape shape4;
Shape shape5;
Shape shape6;
Shape shape7;
Shape shape8;

int x1= mouseX;
int y1 = mouseY;
color red = color(255, 0, 0, 20);
color green = color(0, 255, 0, 30);
color blue = color(0, 0, 255, 10);

int counter = 1;

void setup(){
size (500,500);
smooth();
colorMode(HSB);
shape1 = new Shape(x1, y1, 250, 150, 250, 250, 100, 100);
shape2 = new Shape(x1, y1, 150, 250, 250, 250, 400, 200);
shape3 = new Shape(x1, y1, 400, 250, 250, 250, 100, 400);
shape4 = new Shape(x1, y1, 250, 400, 250, 250, 50, 290);

}

void draw(){
background(250);
shape1.display();
shape2.display();
shape3.display();
shape4.display();
noStroke();

if(counter == 1) {
fill(red);
}
else if(counter == 2) {
fill(green);
}
else if(counter == 3) {
fill(blue);
}

if (mousePressed == true) {
ellipse(mouseX, mouseY, 50, 50);
}

}

void mouseReleased() {
counter++; // increase the counter
if(counter == 4) {
counter = 1; // loop after 3
}
println(counter);
}

//Class

class Shape{

//Variables
int x1, y1, x2, y2, x3, y3, x4, y4;

//The Constuctor
Shape(int px1, int py1, int px2, int py2, int px3, int py3, int px4, int py4){
x1 = px1;
y1 = py1;
x2 = px2;
y2 = py2;
x3 = px3;
y3 = py3;
x4 = px4;
y4 = py4;
}
//Functions

void display(){
//x1 = int(random(x1-10,x1+10));
//y1 = int(random(y1-10,y1+10));
x1 = mouseX;
y1 = mouseY;
quad(x1, y1, x2, y2, x3, y3, x4, y4);

}

}

 

This is the final product:

 

I think this could be improved by designing more organized shapes to create an image perhaps.

Chance Operation – Response

I found this talk incredibly interesting in regards to the merging between softwares and visual arts, because that is a personal interest of mine. His reference to Marshall McLuhan, “we shape our tools and thereafter, our tools shape us”, can be seen as moving further than just software and more into media in general. What I understood from it was that we can try to create something that in return will help us create and produce from it. In terms of software, this is applicable due to the way that we now use the same software to produce art.

Although I enjoyed some parts of the talk, I did not find it engaging, mainly due to the tone of the speaker. However, I cannot help but take away the artistic possibility of working digitally and with software. The examples he showed were incredibly cool and made me think about the type of art I want to see and create.

The Language of New Media: Response

Although I found this a bit of a challenging read because of the jargon, I think some points made throughout the piece were quite logical and rosonated with me. In talking about new media, I think it was interesting the way he divides it into subsections of what it ‘should’ have. The examples used in terms of film and language (and semiotics) made his point of numerical representation more clear to me. I also found the part about removing the human from the creative process interesting, and instead replacing it for automation. His identification of ‘low-level’ and ‘high-level automation shows the difference between different types of automation and what they can mean according to context. I also enjoyed his reference to McLuhan, because I think a lot of his media theory is still valid and applicable in media studies.

Recreating a Design

For this project, I decided to try to recreate this piece:

It was created in 1976 and I really liked the consistent pattern of the lines and circles, rotated slightly differently in each one.

I recreated this design, not without struggle.

This is how my piece turned out:

It’s not exactly the same, my lines are more curved, but I am happy with the overall result- my own take on the piece.