PORTFOLIO YIHAO GEORGE XU TEL: +1 401-743-7680 EMAIL: YXU13@RISD.EDU YIHAO_XU@BROWN.EDU
YIHAO (GEORGE) XU RISD MDES TJU B.ARCHITECTURE +1 401-743-7680 YXU13@RISD.EDU YIHAO_XU@BROWN.EDU
GRAPHIC DESIGN WEB/GAME DEVELOPMENT SERVICE/EXHIBITION WORKS OTHER WORKS UI/UX DESIGN 01 03 02 05 04
UI/UX/WEB DESIGN
ECOHOME HYPOTHESIS
Our group looks to develop a government app to monitor the electricity and water usages in a household. Another function of this app is a platform for energy-saving/generating competitions. This app guides users towards sustainable living by visualizing energy usage on interfaces (phone apps, mirror, AR/VR) and providing a game-like experience.
Drivers Electricity Daily Usage Generate
Typeface Design
Resource Usage Monitor | Augmented Reality | Household | Game-like WHAT OFFICIAL APP TO CONTROL WATER/ELECTRICITY USAGE AT HOME HOME RESOURCE USAGE MONITOR APP WHO WHO USE ANYONE W/ FEE ACCOUNT RESOURCE SERVICE COMPANY ELECTRIC APPLIANCE COMPANIES BATHROOM PRODUCTS COMPANIES GYM EQUIPMENT COMPANIES -APARTMENT (ROOF, SOLAR POWER, PROBLEM OF DISTRIBUTE) -NO GYM EQUIPMENT -THE ELDER, THE DISABLED WHO RUN WHO’S LEFT OUT STAKEHOLDERS
OUR APP ECOHOME IS FOCUSED ON SUSTAINABILITY.
FIRST, WHAT IS ECOHOME? THE FUNDAMENTAL JOB OF ECOHOME IS TO VISUALIZE HOUSEHOLD ENERGY CONSUMPTION AND GENERATION.
SECOND, WHO WILL BE THE STAKEHOLDERS. WE BELIEVE THERE ARE AT LEAST THREE APARTMENTS, WHICH ARE ELECTRIC APPLIANCE COMPANIES, BATHROOM PRODUCTS COMPANIES AND GYM EQUIPMENT COMPANIES.
Case Study
SAVE
IF WATER PIPE LEAK, THEN APP CAN SEND WARNING
NO NEED TO WALK OUTSIDE TO CHECK THE WATER PIPE OR HEATOR
WHERE WHEN WHY NATIONWIDE MONITER TOTAL USAGE AND SPEED COMPETITION RANKING SHARE WITH FRIENDS
EFFORT
BUDGET &
24/7 WEEKLY MONITOR DAILY USAGE GENERATION Many Function Few Function Mechanical Vivd Monitor Social Media Existed Futuristic Open Private Niche Public
In case of water leak
One day , Tom came to home early and found he received a very expensive month bill. He checked the meter outside the house and found that the water heater had been broken.
Tom contacted with water-heater company and was recommended a new APP from National Grid and Providence Water which would prevent this problem in the future.
The new APP can monitor the real time electricity and water usages in a household and send warning message as soon as possible. Two weeks later, Tom picked up his phone and received a notification. Tom immediately contacted the repairman. Luckily, the repairman quickly found the leak location and repaired it.
The new APP can monitor the real time electricity and water usages in a household and send warning message as soon as the leaking happens. customer service agent recommended Tom a new APP om National and Providence Water which acto her would effectively prethis problem. Tom contacted the water heater company to request a partial compensation also to ask for their professional advice on how to prevent this situation in the future. A er receiving the expansive Tom went to check meter and heater outside house. He found the heater leaking. The new APP can monitor the real time electricity and water usages in a household and send warning message as soon as the leaking happens. The customer service agent recommended Tom a new APP om National Grid and Providence Water which according to her would effectively prevent this problem.
Her iend said a new app om Nationalgrid and Providence Water can help her to document and therefore improve on her energy usage.
Lauren comes home and starts a Tiktok live broadcast during which she shows her viewers how she cooks herself a light meal.
During the live, there were negative voice in the comment section judging the way she did the dishes wasting too much water. She lost a lot of followers that night.
Lauren comes home and starts a Tiktok live broadcast during which she shows her viewers how she cooks herself a light meal.
As an Lauren example conservation her audience.
audience.
Two
Lauren comes home from work and starts a Tiktok live broadcast during which
later, Lauren achieved the highest score among the app users in Providence.
During the live, there were negative voice in the comment section judging the way she did the dishes wasted too much water. She lost a lot of followers that night. As an environmental influencer, Lauren feels guilty to set a bad example of energy conservation for her audience. In a couple of days, her friend sent a link of a new app from national grid and Providence Water to her, saying the app can help her to document and therefore take action to improve on her energy usage.
Lauren shares improving saving online encourage her follow ers to too.
During the live, there were negative voice in the comment section judging the way she did the dishes wasting too much water. She lost a lot of followers that night. months
As an Lauren example conservation her In the ney
She received encouraging feedback directly om the reduced number in the data.
on om ducing She was invited to give a TED talk, using the data on this app to validate her improvement on consumption.
MONITOR AND SHARING
THE FUNDAMENTAL FUNCTION OF THIS APP IS TO MONITOR THE APPLIANCE AT HOME, WHICH IS THE MAIN REASON WHY THE HOME IS A SET OF APPLIANCE’S CONDITIONS. THE APP CAN ALSO MONITOR HOW MUCH ENERGY GENERATED. THEREFORE, THE USERS HOW MUCH CONTRIBUTION THE USERS HAS MADE.
MEANWHILE, SOME SOCIAL MEDIA FUNCTIONS ARE ALSO INCLUDED, SINCE THE DESIGNERS WANT TO ATTRACT MORE AND MORE VOLVED IN THIS APP. OF COURSE, THE USERS’ PRIVACY DOES MATTER. TO DEAL WITH THAT, THE DESIGNERS GIVE THE USERS FREEDOM DIFFERENT INFORMATION DETAIL MODE. WITH THE SHARING FUNCTION, THE USERS CAN DISCUSS THEIR ACHIEVEMENTS WITH THEIR AUDIENCES. THEREFORE, THE DESIGNERS WOULD LIKE TO TIE THE MONITOR AND SHARING FUNCTION TOGETHER.
ECOHOME
HOME PAGE OF THIS APP CAN HAVE A CLEAR IDEA
MORE PEOPLE TO GET INFREEDOM TO SELECT THEIR CLOSE FRIENDS OR
GRAPHIC DESIGN
GRAPHIC DESIGN - STUDENT DATA VISUALIZATION
LOCATION: RISD RI
INSTRUCTOR: SACHI TAN - RISD ISSA
www.oiss.risd.edu/coming-to-statistics-2022
Each year there are countless international students coming to RISD. ISSA produces intuitive visualizations based on reliable and solid data. It is hoped that with the help of data visualization tools, students and their parents can better understand the situation of the school.
GRAPHIC DESIGN - WORK FLOW DEMONSTRATION
LOCATION: RISD RI
INSTRUCTOR: SACHI TAN - RISD ISSA
Finding a job is very important for international students. After finding a job, international students need to go through some formalities. These procedures are complex, so the office produces these images to help international students understand the complex procedures.
GRAPHIC DESIGN - WELCOME BANNER
LOCATION: RISD RI
INSTRUCTOR: SACHI TAN - RISD ISSA
https://www.instagram.com/p/CiF6aCxuTY5/?utm_source=ig_web_copy_link
Every new school year begins with many international students coming to RISD. The office will create beautiful banners to welcome these students and congratulate them on coming to the school.
GRAPHIC DESIGN - TRAVEL DOCUMENTS
LOCATION: RISD RI
INSTRUCTOR: SACHI TAN - RISD ISSA
WEB/GAME DEVELOPMENT
NIM
WEB GAME DESIGN
LOCATION: BROWN UNIVERSITY
INSTRUCTOR: JOHN F HUGHES
TYPE: INDIVIDUAL WORK
TIME: 2022 FALL
CONST NUMOFTOTALSTICKS = 21;
LET NUMOFCURRENTSTICKS = NUMOFTOTALSTICKS;
LET ISPLAYER1TURN = TRUE;
CONST GAMEMSGBOX = DOCUMENT.GETELEMENTBYID(“GAMEMSGBOX”);
CONST PLAYER1CONTAINER = DOCUMENT.GETELEMENTBYID(“PLAYER1CONTAINER”);
CONST PLAYER1BTN = DOCUMENT.GETELEMENTBYID(“PLAYER1BTN”);
CONST PLAYER1TEXT = DOCUMENT.GETELEMENTBYID(“PLAYER1INPUT”);
CONST PLAYER2CONTAINER = DOCUMENT.GETELEMENTBYID(“PLAYER2CONTAINER”);
CONST PLAYER2BTN = DOCUMENT.GETELEMENTBYID(“PLAYER2BTN”);
CONST PLAYER2TEXT = DOCUMENT.GETELEMENTBYID(“PLAYER2INPUT”);
// INT => ARRAY(INT)
LET GETLEGALMOVE = (NUMOFCURRENTSTICKS) => {
SWITCH (NUMOFCURRENTSTICKS) {
CASE 1: RETURN [1];
CASE 2: RETURN [1, 2];
DEFAULT:
RETURN [1, 2, 3]; }
LET OTHERPLAYER = () => {
PLAYER1TEXT.VALUE = “”; PLAYER2TEXT.VALUE = “”;
IF (ISPLAYER1TURN) { ISPLAYER1TURN = FALSE;
PLAYER1CONTAINER.CLASSNAME = “PLAYER-INPUT-DISENABLED”;
PLAYER2CONTAINER.CLASSNAME = “PLAYER-INPUT-ENABLED”; } ELSE { ISPLAYER1TURN = TRUE;
PLAYER1CONTAINER.CLASSNAME = “PLAYER-INPUT-ENABLED”; PLAYER2CONTAINER.CLASSNAME = “PLAYER-INPUT-DISENABLED”; }
LET NEXTMOVE = () => {
//CONST CURRENTPLAYERNAME = ISPLAYER1TURN? “PLAYER1” : “PLAYER2”; CONST NUMOFPICKED = ISPLAYER1TURN ? PARSEINT(PLAYER1TEXT.VALUE) : PARSEINT(PLAYER2TEXT.VALUE); CONSOLE.LOG(NUMOFPICKED); CONSOLE.LOG(GETLEGALMOVE(NUMOFCURRENTSTICKS));
IF (GETLEGALMOVE(NUMOFCURRENTSTICKS).INCLUDES(NUMOFPICKED)) {
NUMOFCURRENTSTICKS -= NUMOFPICKED;
IF (NUMOFCURRENTSTICKS == 0) {
// CURRENT PLAYER LOST; GAME OVER GAMEMSGBOX.INNERTEXT = ISPLAYER1TURN ? “PLAYER 2 WIN !” : “PLAYER 1 WIN !”; PLAYER1CONTAINER.CLASSNAME = “PLAYER-INPUT-DISENABLED”; PLAYER2CONTAINER.CLASSNAME = “PLAYER-INPUT-DISENABLED”;
} ELSE { // GAME ONGOING GAMEMSGBOX.INNERHTML = `NOW THERE ARE <B>${NUMOFCURRENTSTICKS}</B> ON THE TABLE. <BR> IT IS ${ISPLAYER1TURN ? “PLAYER2” : “PLAYER1”}’S TURN`; OTHERPLAYER();
} } ELSE { CONSOLE.LOG(“ERROR: PLEASE PICK LEGAL MOVES”); GAMEMSGBOX.INNERHTML = `NOW THERE ARE <B>${NUMOFCURRENTSTICKS}</B> ON THE TABLE. <BR> IT IS ${ISPLAYER1TURN ? “PLAYER2” : “PLAYER1”}’S TURN.<BR> PLEASE PICK ${GETLEGALMOVE(NUMOFCURRENTSTICKS)} STICK(S)`;
IF (ISPLAYER1TURN) {
PLAYER1CONTAINER.CLASSNAME = “PLAYER-INPUT-WARNING”; PLAYER1TEXT.VALUE = “”;
} ELSE { PLAYER2CONTAINER.CLASSNAME = “PLAYER-INPUT-WARNING”
PLAYER2TEXT.VALUE = “”; } }
LET RELEASEWARNING = () => {
IF (ISPLAYER1TURN) {
IF (PLAYER1CONTAINER.CLASSNAME == “PLAYER-INPUT-WARNING”) { PLAYER1CONTAINER.CLASSNAME = “PLAYER-INPUT-ENABLED”; } } ELSE {
IF (PLAYER2CONTAINER.CLASSNAME == “PLAYER-INPUT-WARNING”) { PLAYER2CONTAINER.CLASSNAME = “PLAYER-INPUT-ENABLED”; } }
PLAYER1BTN.ADDEVENTLISTENER(“CLICK”, NEXTMOVE); PLAYER2BTN.ADDEVENTLISTENER(“CLICK”, NEXTMOVE); PLAYER1TEXT.ADDEVENTLISTENER(“CLICK”, RELEASEWARNING); PLAYER2TEXT.ADDEVENTLISTENER(“CLICK”, RELEASEWARNING);
};
};
};
};
12/26/22, 2:41 PM
function _1(md) {
return (
md``/*`# Sequences Sunburst
September Total Enrolled Internationa Students F1 Visa 5 54%
Sequences Sunburst
This example shows how it is possible to use a [sunburst visualization](https://observablehq.com/@d3/sunburst) with data that describes sequences of events. Hover over the segments to see the corresponding sequences.
A good use case is to summarize navigation paths through a web site or app, as in the sample data file (which is attached to the \`csv\` cell). Where a funnel lets you understand a single pre-selected path, this allows you to see all possible paths. For example, you might want to compare visits that start directly on a product page (e.g. after landing there from a search engine) to visits where users arrive on the site’s home page and navigate from there.
See also: the non-radial counterpart to this, [Sequences Icicle](/@kerryrodden/sequences-icicle). */)
}
svg
function _breadcrumb(d3, breadcrumbWidth, breadcrumbHeight, sunburst, breadcrumbPoints, color) { const svg = d3
.create(“svg”)
.attr(“viewBox”, `0 0 ${breadcrumbWidth * 10} ${breadcrumbHeight}`)
.style(“font”, “6px sans-serif”) // level font-size
.style(“margin”, “5px”);
const g = svg
.selectAll(“g”)
.data(sunburst.sequence)
.join(“g”)
.attr(“transform”, (d, i) => `translate(${i * breadcrumbWidth}, 0)`);
g.append(“polygon”)
.attr(“points”, breadcrumbPoints)
.attr(“fill”, d => color(d.data.name))
.attr(“stroke”, “white”);
g.append(“text”)
.attr(“x”, (breadcrumbWidth + 10) / 2)
.attr(“y”, 15)
.attr(“dy”, “0.35em”)
.attr(“text-anchor”, “middle”)
.attr(“fill”, “white”)
.text(d => d.data.name);
svg
.append(“text”)
.append(“g”)
.attr(“fill”, “none”)
.attr(“pointer-events”, “all”)
.on(“mouseleave”, () => { path.attr(“fill-opacity”, 1); label.style(“visibility”, “hidden”);
// Update the value of this view
element.value = { sequence: [], percentage: 0.0 }; element.dispatchEvent(new CustomEvent(“input”)); })
.selectAll(“path”)
.data(
root.descendants().filter(d => {
// Don’t draw the root node, and for efficiency, filter out return d.depth && d.x1 - d.x0 > 0.001; })
) .join(“path”)
.attr(“d”, mousearc) .on(“mouseenter”, (event, d) => {
// Get the ancestors of the current segment, minus the root const sequence = d .ancestors() .reverse() .slice(1);
F1 Visa: 904 cases dealed with
.text(sunburst.percentage > 0 ? sunburst.percentage + “%” : “”) // horizontal level icon
.attr(“x”, (sunburst.sequence.length + 0.5) * breadcrumbWidth)
.attr(“y”, breadcrumbHeight / 2)
.attr(“dy”, “0.35em”)
.attr(“text-anchor”, “middle”)
.attr(“fill”, “white”); // horizontal text color
return svg.node(); }
function _sunburst(partition, data, d3, radius, width, color, arc, mousearc) {
const root = partition(data);
const svg = d3.create(“svg”);
// Highlight the ancestors path.attr(“fill-opacity”, node => sequence.indexOf(node) >= 0 ? 1.0 : 0.3 ); const percentage = ((100 * d.value) / root.value).toPrecision(3); label
.style(“visibility”, null) .select(“.percentage”)
.text(/*percentage + “% “ + */d.data.name + “: “ + d.value);
// Update the value of this view with the currently hovered element.value = { sequence, percentage }; element.dispatchEvent(new CustomEvent(“input”)); });
return element; }
// Make this into a view, so that the currently hovered sequence is available to the breadcrumb
const element = svg.node();
element.value = { sequence: [], percentage: 0.0 };
const label = svg
.append(“text”)
.attr(“text-anchor”, “middle”)
.attr(“fill”, “white”) // center text color
.style(“visibility”, “hidden”);
label .append(“tspan”)
.attr(“class”, “percentage”)
.attr(“x”, 0)
.attr(“y”, 0)
.attr(“dy”, “-0.1em”)
.attr(“font-size”, “2em”)
.text(“”);
label
.append(“tspan”)
.attr(“x”, 0)
.attr(“y”, 0)
.attr(“dy”, “1.5em”)
.text(“cases dealed with”);
svg
function _4(md) { return ( md``/*`
Features:
* works with data that is in a CSV format (you don’t need to pre-generate - in Observable, you can just replace the file that’s attached to
* interactive breadcrumb trail helps to emphasize the sequence, seeing
* percentages are shown explicitly, to help overcome the distortion
If you want to reuse this with your own data, here are some tips
* no header is required (but it’s OK if one is present)
* use a hyphen to separate the steps in the sequence
* every sequence should have an “end” marker as the last element, sequence length (6, in the example). The purpose of the “end” marker from an end point that has been forced by truncation.
* each line should be a complete path from root to leaf - don’t search-end” and “home-search-product-end” but not “home-search” counts of all the sequences with that prefix.
* to keep the number of permutations low, use a small number of of either of these will lead to a very large CSV that will be slow
* keep the step names short */) }
sunburst = Object {sequence: Array(3), percentage: "5.54"}
.attr(“viewBox”, `${-radius} ${-radius} ${width} ${width}`)
.style(“max-width”, `${width}px`)
.style(“font”, “6px sans-serif”); // center font-size
csv = Array(288) [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2),
const path = svg
.append(“g”)
.selectAll(“path”)
async function _csv(d3, FileAttachment) { return ( d3.csvParseRows(await FileAttachment(“visit-sequences@1.csv”).text()) ) }
data = Object {name: "root", children: Array(12)}
.data(
partition = ƒ(data)
color = ƒ(i)
width = 640
radius = 320
arc = ƒ()
function _partition(d3, radius) { return ( data => d3.partition().size([2 * Math.PI, radius * radius])( d3
.hierarchy(data)
.sum(d => d.value) //.sort((a, b) => b.value - a.value) //sort by case numbers ) ) }
function _color(d3) { return ( d3
.scaleOrdinal()
.domain([“Total Enrolled International Students”, “Total Graduated Student Served”, “Total Advising Meetings Held”, “Applications Processed”, “Letters Issued”, “Reporting”, “F1 Visa”, “OPT (approved only)”])
.range([“#09788B”, “#37b1af”, “#ffc501”, “#6fb971”, “#cd5eb9ff”, “#bbbbbb”, “#53cdcb”, “#b7e4e4”])
}
) // pie chart color scheme
function _width() { return ( 640 )
}
}
function _buildHierarchy() { return ( function buildHierarchy(csv) {
function _radius(width) {
return ( width / 2 )
}
function _arc(d3, radius) {
return (
d3
.arc()
out nodes that would be too small to see root root.value).toPrecision(3); // ?
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.padAngle(0) //padding
.padRadius(radius)
.innerRadius(d => Math.sqrt(d.y0))
.outerRadius(d => Math.sqrt(d.y1) - 0) //gap ) }
d.value); // center percentage text + value hovered sequence and percentage
function _arcMonthText(d3, radius) { return ( d3
.arc()
.innerRadius(radius)
.outerRadius(radius)
.text(d => d.data.name) ) }
function _mousearc(d3, radius) { return ( d3 .arc()
pre-generate a hierarchical JSON file, unless your data file is very large) to the \`csv\` cell so that it is easy for a first-time user to understand what they are distortion of the data that occurs when using a radial presentation tips for generating the CSV file:
element, *unless* it has been truncated because it is longer than the maximum marker is to distinguish a true end point (e.g. the user left the site) include counts for intermediate steps. For example, include “home- the latter is computed by the partition layout, by adding up the unique step names, and a small maximum sequence length. Larger numbers slow to process.
// Helper function that transforms the given CSV into a hierarchical format. const root = { name: “root”, children: [] }; for (let i = 0; i < csv.length; i++) { const sequence = csv[i][0]; const size = +csv[i][1]; if (isNaN(size)) { // e.g. if this is a header row continue;
}
const parts = sequence.split(“-”);
let currentNode = root;
for (let j = 0; j < parts.length; j++) { const children = currentNode[“children”]; const nodeName = parts[j]; let childNode = null; if (j + 1 < parts.length) {
// Not yet at the end of the sequence; move down the tree. let foundChild = false; for (let k = 0; k < children.length; k++) { if (children[k][“name”] == nodeName) { childNode = children[k]; foundChild = true; break;
}
}
// If we don’t already have a child node for this branch, create it. if (!foundChild) { childNode = { name: nodeName, children: [] }; children.push(childNode); } currentNode = childNode;
} else {
// Reached the end of the sequence; create a leaf node. childNode = { name: nodeName, value: size }; children.push(childNode);
} } } return root; } ) }
function _breadcrumbWidth() { return ( 75 ) }
function _breadcrumbHeight() { return ( 30 ) }
function _breadcrumbPoints(breadcrumbWidth, breadcrumbHeight) { return ( function breadcrumbPoints(d, i) { const tipWidth = 10; const points = []; points.push(“0,0”);
, Array(2), Ar
FileAttachment(“visit-sequences@1.csv”).text())
https://designasabaker.github.io/ISSADataD3Visualization/
data visualization on office cases data
D3.js