| Total net profit | -49.1 | Gross profit | 32.24 | Gross loss | -81.34 |
| Profit factor | 0.4 | Expected payoff | -0.64 | ||
| Absolute drawdown | -53.46 | Maximal drawdown (%) | 63.13 (652.84%) | ||
| Total trades | 77 | Profit trades (% of total) | 5 (6.49%) | Loss trades (% of total) | 72 (93.51%) |
| Long positions (won %) | 45 (11.11%) | Short positions (won %) | 32 (0.0%) | ||
| Largest | profit trade | 9.41 | loss trade | -2.73 | |
| Average | profit trade | 6.45 | loss trade | -1.13 | |
| Minimum | profit trade | 2.17 | loss trade | -0.57 | |
| Maximum | take profit in pips | 0.0 | stop loss in pips | -16.1 | |
| Average | take profit in pips | 0.0 | stop loss in pips | -10.7 | |
| Minimum | take profit in pips | 0.0 | stop loss in pips | -5.6 | |
| Maximum | Reward-Risk | 0.0 | Duration in minutes | 10375 | |
| Average | Reward-Risk | 0.0 | Duration in minutes | 690.8 | |
| Minimum | Reward-Risk | 0.0 | Duration in minutes | 3 | |
| Maximum | consecutive wins (profit in money) | 3.0 (27.88) | consecutive losses (loss in money) | 54.0 (-63.13) | |
| Average | consecutive wins | 3.0 | consecutive losses | 36.0 |
| Total net profit | -13.81 | Gross profit | 32.24 | Gross loss | -46.05 |
| Profit factor | 0.7 | Expected payoff | -0.31 | ||
| Absolute drawdown | -18.17 | Maximal drawdown (%) | 40.29 (182.14%) | ||
| Total trades | 45 | Profit trades (% of total) | 5 (11.11%) | Loss trades (% of total) | 40 (88.89%) |
| Largest | profit trade | 9.41 | loss trade | -2.73 | |
| Average | profit trade | 6.45 | loss trade | -1.15 | |
| Minimum | profit trade | 2.17 | loss trade | -0.59 | |
| Maximum | take profit in pips | 0.0 | stop loss in pips | -14.2 | |
| Average | take profit in pips | 0.0 | stop loss in pips | -10.6 | |
| Minimum | take profit in pips | 0.0 | stop loss in pips | -5.9 | |
| Maximum | Reward-Risk | 0.0 | Duration in minutes | 10375.0 | |
| Average | Reward-Risk | 0.0 | Duration in minutes | 1059.3 | |
| Minimum | Reward-Risk | 0.0 | Duration in minutes | 3.0 | |
| Maximum | consecutive wins (profit in money) | 3.0 (27.88) | consecutive losses (loss in money) | 34.0 (-40.29) | |
| Average | consecutive wins | 3.0 | consecutive losses | 20.0 |
| Total net profit | -35.29 | Gross profit | 0.0 | Gross loss | -35.29 |
| Profit factor | 0.0 | Expected payoff | -1.1 | ||
| Absolute drawdown | -35.29 | Maximal drawdown (%) | 34.24 (-3260.95%) | ||
| Total trades | 32 | Profit trades (% of total) | 0 (0.0%) | Loss trades (% of total) | 32 (100.0%) |
| Largest | profit trade | 0.0 | loss trade | -1.62 | |
| Average | profit trade | 0.0 | loss trade | -1.1 | |
| Minimum | profit trade | 0.0 | loss trade | -0.57 | |
| Maximum | take profit in pips | 0.0 | stop loss in pips | -16.1 | |
| Average | take profit in pips | 0.0 | stop loss in pips | -10.9 | |
| Minimum | take profit in pips | 0.0 | stop loss in pips | -5.6 | |
| Maximum | Reward-Risk | 0.0 | Duration in minutes | 835.0 | |
| Average | Reward-Risk | 0.0 | Duration in minutes | 172.6 | |
| Minimum | Reward-Risk | Duration in minutes | 9.0 | ||
| Maximum | consecutive wins (profit in money) | 0.0 (0.0) | consecutive losses (loss in money) | 32.0 (-35.29) | |
| Average | consecutive wins | 0.0 | consecutive losses | 32.0 |


{% extends "_base.html" %} {% block content %} Todos GET Read POST Task Completed? Create PUT ID Task Completed? Update DELETE ID Delete {% endblock content %} {% block javascript %} {% endblock %}


jsQR decoder comparison
This is a comparison of different js QR code decoders.
Library
Result
jsQR
jsqrcode


All Classes
-
All Classes Class Summary Enum Summary Class Description AbstractShapePanel A common base for all the panels which draw a user-movable shape specifying common properties.CirclePanel A panel which allows the user to change the size of the circleCirclePanel.Circle Internal class containing properties of the circle drawn in theCirclePanel
ClosestPoint Used to keep track of the point closest to the selected point of a figure in anAbstractShapePanel
when selected using a mouse.DrawingPanel Panel which supports mouse-driven change in position and size of various shapesEditor A text editor that usesStyledDocument
for setting attributes to each character.FontSelector A Panel from which a user can select a font from all the Fonts installed in his/her computer.HexagonPanel A panel which draws a hexagon.Main The Main class for the Project.OvalPanel A panel which draws an oval.RectanglePanel A panel which draws a rectangle.Shape Shapes supported byDrawingPanel
StylizedClipboard A clipboard used by theStylizedTextPane
.StylizedClipboard.StylizedCharacter A container for the attributes of a styled characterStylizedTextPane AJTextPane
that uses aStyledDocument
to store formatted text.TrianglePanel A panel which draws a triangle.


Preparation time
less than 30 mins
Cooking time
less than 10 mins
Serves
Serves 4
Homemade fish nuggets make a fun children’s tea or great snack food for guests, with a delicious, freshly made tartare sauce.
For the fish, fill a deep-fat fryer or a large, deep, heavy-bottomed pan two-thirds full with vegetable oil. Heat the oil to 190C or until a breadcrumb sizzles and turns golden-brown when dropped into it (CAUTION: Hot oil can be dangerous. Don't leave unattended).
Sprinkle the flour onto a plate. Beat the eggs in a bowl. Sprinkle the panko breadcrumbs onto a separate plate. Dredge the fish in flour, dusting off any excess, then dip them in the egg, then roll in the breadcrumbs until coated.
Cook in the deep-fat fryer for 2-3 minutes until golden-brown.
For the tartare sauce, whisk the egg yolks and vinegar together in a bowl.
Continue to whisk the mixture and slowly pour in the rapeseed oil until all of the oil is incorporated and mixture is thick and light.
Stir in the capers, gherkins, shallot and herbs. Season, to taste, with salt and pepper and a squeeze of lemon juice.
Serve the fish goujons with the tartare sauce and lemon wedges.


JOKER
18 | 2h 2min | Crime, Drama, Thriller | 3 October 2019 (Brazil)
Director: Todd Phillips
Writers: Todd Phillips and Scott Silver
Stars: Joaquin Phoenix, Zazie Beetz, Robert De Niro e outros
Premise: In 1981, Arthur Fleck (Joker), a failed stand-up comedian, turns to a life of crime and chaos in Gotham City.
THE IRISHMAN
18 | 3h 30min | Biography, Crime, Drama | 27 November 2019 (Brazil)
Director: Martin Scorsese
Writers: Charles Brandt and Steven Zaillian
Stars: Robert De Niro, Al Pacino, Joe Pesci e outros
Premise: The Irishman is the story of Frank Sheeran, a mob hitman and World War II vet who develops his skills during his service in Italy. Now an old man, he reflects on the events that defined his career as a hitman, particularly the role he played in the disappearance of labor leader Jimmy Hoffa, his longtime friend, and his involvement with the Bufalino crime family.
THE KINGSMAN
12 | Action, Adventure, Comedy | 13 February 2020 (Brazil)
Director: Matthew Vaughn
Writers: Matthew Vaughn and Jane Goldman
Stars: Ralph Fiennes, Harris Dickinson, Gemma Arterton e outros
Premise:As a collection of history's worst tyrants and criminal masterminds gather to pilot a war to wipe out millions, one man must race against time to stop them .
AVATAR 2
12 | Action, Adventure, Fantasy | 17 Decembr 2021 (Brazil)
Director: James Cameron
Writers: James Cameron and Josh Friedman
Stars: Zoe Saldana, Sam Worthington, Stephen Lang e outros


Week 4 Meal Prep
Well, I am on Week 4 and I still have the enthusiasm of Week 2 with the meal prepping skills of Week 1. This simple meal prep took me about 2 hours longer than it needed to, partly due to the fact that I thought I could watch the Australian bachelorette while chopping and this week I decided to take pictures along the way in case anyone wants to follow along. Click here to see meal prep week 4 edition .


> console.log
For general output of logging information. You may use string substitution and additional arguments with this method.
> Examples about the javascript console object.
For general output of logging information. You may use string substitution and additional arguments with this method.
Informative logging of information. You may use string substitution and additional arguments with this method.
Outputs a warning message. You may use string substitution and additional arguments with this method.
Outputs an error message. You may use string substitution and additional arguments with this method.


An exhibit of Markdown 😎
This note demonstrates some of what Markdown is capable of doing.
Note: Feel free to play with this page. Unlike regular notes, this doesn't automatically save itself.
Basic formatting
Paragraphs can be written like so. A paragraph is the basic block of Markdown. A paragraph is what text will turn into when there is no reason it should become anything else.
Paragraphs must be separated by a blank line. Basic formatting of italics and bold is supported. This can be nested like so.
Lists
Ordered list
- Item 1
- A second item
- Number 3
- Ⅳ
Note: the fourth item uses the Unicode character for Roman numeral four.
Unordered list
- An item
- Another item
- Yet another item
- And there's more...
Paragraph modifiers
Code block
Code blocks are very useful for developers and other people who look at code or other things that are written in plain text. As you can see, it uses a fixed-width font.
You can also make inline code
to add code into other things.
Quote
Here is a quote. What this is should be self explanatory. Quotes are automatically indented when they are used.
Headings
There are six levels of headings. They correspond with the six levels of HTML headings. You've probably noticed them already in the page. Each level down uses one more hash character.
Headings can also contain formatting
They can even contain inline code
Of course, demonstrating what headings look like messes up the structure of the page.
I don't recommend using more than three or four levels of headings here, because, when you're smallest heading isn't too small, and you're largest heading isn't too big, and you want each size up to look noticeably larger and more important, there there are only so many sizes that you can use.
URLs
URLs can be made in a handful of ways:
- A named link to Proser. The
easiest way to do these is to select what you want to make a link and hit
Ctrl+L
. - Another named link to Proser
- Sometimes you just want a URL like https://proser.kash.im/.
Horizontal rule
A horizontal rule is a line that goes across the middle of the page.
It's sometimes handy for breaking things up.
Images
Markdown can also contain images. I'll need to add something here sometime.
Finally
There's actually a lot more to Markdown than this. See the official introduction and syntax for more information. However, be aware that this is not using the official implementation, and this might work subtly differently in some of the little things.


I'm not a expert.
So, please, if I make any mistake, just tell me so!
I'm just a Svelte lover and I want to spread the word...
(() => {
const square = number => number * number;
const secondNumber = square(firstNumber);
const firstNumber = 42;
console.log(secondNumber);
})();
$: foo = bar
// in vanilla js
let foo = 10
let bar = foo + 10 // bar = 20
foo = bar // bar = 20 no reactivity
bar = foo + 10 // now bar becomes 25
// in Svelte
let foo = 10
$: bar = foo + 10 // now bar is 20
foo = 15
// bar is now 25 because it is bound to the value of foo
What year were you born?
{#if age}
You are {age} years old
{/if}
13 lines of codes
import React, {Component} from 'react'
export default () => {
const {age, setAge} = useState(0)
const {year, setYear} = useState(0)
function calculateAge(){
const currentYear = new Date().getFullYear()
setAge(currentYear - year)
}
function handleChange(event){
setYear(event.target.value)
}
return(
What year were you born?
{age ? You are {age} years old
: null}
)}
25 lines of code
Svelte compiles your code into low-level, pure javascript that will interact directly with the DOM
npx degit sveltejs/template my-svelte-project
cd my-svelte-project
npm install
npm run dev
You write your code in a .svelte file
Hello {name}!
// main.js
import App from './App.svelte';
const app = new App({
target: document.body
});
export default app;
// main.js
import App from './App.svelte';
import App2 from './App2.svelte';
const app = new App({
target: document.getElementById('div1')
});
const app2 = new App({
target: document.getElementById('div2')
});
export default app;
Scope styles
// App.svelte
These styles...
// Nested.svelte
{name}
This is reactive variable {foo}
{#if condition} {:else if} {:else} {/if}
{#if x >= 10}
{x} is greater than 10
{:else if x > 5}
{x} is greater than 10
{:else}
{#if 5 > x}
{x} is less than 5
{:else}
{x} is between 5 and 10
{/if}
{/if}
{#each yourArray as yourSingleElement} {/each}
The Famous Cats of YouTube
{#each cats as cat}
-
{cat.name}
{/each}
on:message={thenameyouwant}
bind:value={yourValue}
{a} + {b} = {a + b}
In a theme or a plugin


klippy
tooltip messages can be customized. New default message.
klippy
color can be customized (#13).
klippy
position can be modified (#10).
klippy
now renders well with Hugo Academic (thanks to Emerson M. Del Ponte, #8).tooltips are rendered with Primer Tooltips instead of Bootstrap Tooltips (removes Bootstrap dependency).
javascript code of klippy
written in Vanilla JS (removes jQuery dependency).
creates klippy_dependencies
function.
klippies
are auto-sized. Argument size
is removed.
klippy
icon is changed: octicons’ clippy.svg
is used instead of glyphicon
.
Remove the distribution of jQuery
and Bootstrap
: calls to rmarkdown::html_dependency_*
are preferred.
klippy
moved to upper left corners of code blocks (thx @ Elin Waring for the suggestion)
Add a size
argument to klippy()
Documentation provides a more comprehensive example


Function
| Static Public Summary | ||
| public |
collapse(data: *): * |
|
| public |
Connect to an XAPI endpoint. |
|
| public |
connectSSH(options: object): Duplex Creates a Duplex SSH stream. |
|
| public |
createCommandResponse(response: *): * |
|
| public |
createErrorResponse(id: *, error: *): {"jsonrpc": *, "id": *, "error": *} |
|
| public |
createGetResponse(request: *, response: *): * |
|
| public |
createRequest(id: *, method: *, params: *): * |
|
| public |
createResponse(id: *, result: *): {"jsonrpc": *, "id": *, "result": *} |
|
| public |
createSetResponse(request: *, response: *): * |
|
| public |
Extend {Base} class and apply {Mixins}. |
|
| public |
normalizePath(path: Array | string): Array Normalizes a path by turning it into an Array of strings. |
|
| public |
parseFeedbackResponse(response: *): * |
|
| public |
Synchronous frontend to JSONparser. |
|
| public |
Use the TSH binary to connect to a TSH server. |
Static Public
public collapse(data: *): * source
import {collapse} from 'jsxapi/lib/xapi/rpc.js'
Params:
| Name | Type | Attribute | Description |
| data | * |
Return:
| * |
public connect(url: string, options: Object): XAPI source
import {connect} from 'jsxapi'
Connect to an XAPI endpoint.
Params:
| Name | Type | Attribute | Description |
| url | string | Connection specification. |
|
| options | Object |
|
Connect options. |
| options.host | string |
|
Hostname to connect to. |
| options.username | string |
|
Username to authenticate with if protocol requires authentication. |
| options.password | string |
|
Password to authenticate with if protocol requires authentication. |
| options.loglevel | string |
|
Set the internal log level. |
Example:
const xapi = connect('ssh://host.example.com:22');
public connectSSH(options: object): Duplex source
import connectSSH from 'jsxapi/lib/transport/ssh.js'
Creates a Duplex SSH stream.
Params:
| Name | Type | Attribute | Description |
| options | object | ||
| options.host | string | Hostname or IP address. |
|
| options.port | number | Port to connect to. |
|
| options.username | string | Username used in authentication. |
|
| options.password | string | Password used in authentication. |
|
| options.command | string | If command is specified, it is executed on the remote host instead of a login shell. |
public createCommandResponse(response: *): * source
import {createCommandResponse} from 'jsxapi/lib/xapi/rpc.js'
Params:
| Name | Type | Attribute | Description |
| response | * |
Return:
| * |
public createErrorResponse(id: *, error: *): {"jsonrpc": *, "id": *, "error": *} source
import {createErrorResponse} from 'jsxapi/lib/xapi/rpc.js'
Params:
| Name | Type | Attribute | Description |
| id | * | ||
| error | * |
Return:
| {"jsonrpc": *, "id": *, "error": *} |
public createGetResponse(request: *, response: *): * source
import {createGetResponse} from 'jsxapi/lib/xapi/rpc.js'
Params:
| Name | Type | Attribute | Description |
| request | * | ||
| response | * |
Return:
| * |
public createRequest(id: *, method: *, params: *): * source
import {createRequest} from 'jsxapi/lib/xapi/rpc.js'
Params:
| Name | Type | Attribute | Description |
| id | * | ||
| method | * | ||
| params | * |
Return:
| * |
public createResponse(id: *, result: *): {"jsonrpc": *, "id": *, "result": *} source
import {createResponse} from 'jsxapi/lib/xapi/rpc.js'
Params:
| Name | Type | Attribute | Description |
| id | * | ||
| result | * |
Return:
| {"jsonrpc": *, "id": *, "result": *} |
public createSetResponse(request: *, response: *): * source
import {createSetResponse} from 'jsxapi/lib/xapi/rpc.js'
Params:
| Name | Type | Attribute | Description |
| request | * | ||
| response | * |
Return:
| * |
public mix(Base: Object, Mixins: Array): * source
import {mix} from 'jsxapi/lib/xapi/mixins.js'
Extend {Base} class and apply {Mixins}.
Return:
| * | Object - New ad-hoc base class with mixins applied. |
public normalizePath(path: Array | string): Array source
import normalizePath from 'jsxapi/lib/xapi/normalizePath.js'
Normalizes a path by turning it into an Array of strings. Removes empty parts of the path and ignores redundant whitespace or slashes. Each path element is also capitalized.
public parseFeedbackResponse(response: *): * source
import {parseFeedbackResponse} from 'jsxapi/lib/xapi/rpc.js'
Params:
| Name | Type | Attribute | Description |
| response | * |
Return:
| * |
public parseJSON(json: string): Object source
import {parseJSON} from 'jsxapi/lib/json-parser.js'
Synchronous frontend to JSONparser.
Params:
| Name | Type | Attribute | Description |
| json | string | JSON string input. |


Mogul Wind NGO is a startup movement to spread the idea of entrepreneurship, innovation and creativity.
Our Vision is to helps startup companies to develop their business successfully by providing services such as networking events, workshops, and so on.
In other word "startup incubator".
Now we are focusing on to help Mongolian fellow entrepreneurs to reach the Japanese market, on the other hand, Japanese entrepreneurs to reach the Mongolian market.
And my team has successfully organized 10+ events, our community growing to 450+ in one year. Check it out
We did organize a variety of events for Mongolian students in Japan such as sport festival, new year party, and so on. Check it out
Startup Weekend is a global event to build a startup from scratch for 54 hour.
We tried to solve a problem of people's anxiety of burglary in the coffee shop. We made a IoT device to solve this problem and pitched in front of VCs. Check it out
Harumatsuri is the biggest overseas festival of Mongolia. It is a great opportunity to showcase Mongolian culture to Japanese people.
Check it outI watched this book's author's TED video and inspired the idea of making creator not consumer.
So, I am in the process of translating this children's book. Check it out


Sara Wallis: It's easy to have a bawl for Pride of Britain Awards
Even the stars in the audience at the Pride of Britain Awards were battling their tear ducts to save their immaculate make-up
by Sara Wallis, https://www.facebook.com/SaraWallisMirror/From the moment 11-year-old Corrie superfan Dante started body popping with Ashley Banjo, right through to Emili Sandé singing out the show, the Daily Mirror’s Pride of Britain had me in a state.
The two-hour ITV event is always a bit like running an emotional marathon. But this 20th anniversary ceremony broke me at around the four-minute mark – and didn’t let up.
It was one astonishing story after another.
Each an example of unbelievable heroics, courage or dedication.
Each with a heart-wrenching video or dramatic reconstruction.
Each ending with the winner’s celebrity favourite surprising the bejeezus out of them – and another mascara stain down my puffy red cheeks.
When Dante realised the man inside the Corrie mascot was Jack P Shepherd, aka David Platt, his idol, it was telly joy.
The unsung heroes become the stars at Pride of Britain, with the celebs further down the pecking order for once.
Actors, singers and footballers forced to acknowledge that kicking a ball about isn’t quite as heroic as say... pulling three people from a burning plane wreckage.
Yes, winners Daniel Nicholson and Joel Snarr did actually do that, rescuing the survivors just seconds before it exploded.
It sounded like a disaster movie.
Nine-year-old schoolboy Ben Hedger won a Child of Courage award for steering his mum’s car across a dual carriageway when she collapsed at the wheel doing 65mph. “The car was getting faster each second,” he said.
Jaydee-Lee Dummett, seven, saved her entire family by raising the alarm after spotting signs of carbon monoxide poisoning.
And Avengers fan Aaron Hunter, who is nine, recruited Robert Downey Jr to fundraise in the hope of finding a cure for his rare terminal condition Rohhad syndrome, raising more than £300,000.
Stars in the audience were battling their tear ducts to save their immaculate make-up. Lots of stoic blinking going on.
Of course the famous faces need to be given a bit of credit for some golden TV moments.
Craig Revel Horwood broke character to give Makaton founder Margaret Walker a smile, thumbs up and a “Fab-U-Lous”. He’ll need to lie down after that.
Ant and Dec popped up behind two newspapers to give Jaydee-Lee the shock of her life.
Janet Jackson made the Lifetime Achievement winner gasp out loud, while Liam Payne made little Ben burst into tears of happiness.
Inspiring – and not remotely connected to Brexit – this was a welcome dose of perspective and human kindness.


Here are the 2020 Democratic presidential candidates who will be on stage at tonight's debate in Atlanta, Business Insider - Business Insider Singapore
by Grace Panetta, Business Insider US- Ten candidates have qualified for the fifth Democratic primary debate hosted by MSNBC and the Washington Post tonight, November 20 in Atlanta, Georgia.
- Candidates needed both 165,000 individual donors and to earn either 3% in four DNC-approved national polls or 5% in two approved early-state polls, leaving some candidates off the stage.
- Former Rep. Beto O’Rourke dropped out of the race on November 1 after failing to qualify for the November debate and former HUD Secretary Julian Castro attained zero polls, meaning he’ll also miss the stage.
- Visit Business Insider’s homepage for more stories.
Ten candidates have qualified for tonight’s fifth Democratic primary debate on November 20, which will be hosted by MSNBC and the Washington Post in Atlanta, Georgia.
In order to qualify for the November debate, candidates needed both 165,000 individual donors and to earn either 3% in four DNC-approved national polls or 5% in two approved early-state polls from Iowa, New Hampshire, South Carolina, or Nevada.
Polls released between September 13 and November 13 – the week before the debate – counted towards qualification.
So far, ten candidates have met both requirements, according to Politico’s Democratic primary poll tracker.
But the stricter polling requirements leaft some mid-tier candidates who easily qualified for the September and October debates completely off the stage.
Former Rep. Beto O’Rourke dropped out of the race on November 1 after failing to qualify for the November debate and former HUD Secretary Julian Castro attained zero polls, meaning he’ll also miss the stage. Castro recently threatened to drop out of the race altogether if he doesn’t raise $800,000 by Halloween.
Sen. Kamala Harris
Read more about Kamala Harris’ campaign.
Sen. Amy Klobuchar
Read more about Amy Klobuchar’s campaign.
Andrew Yang
Read more about Andrew Yang’s campaign.
Sen. Cory Booker
Read more about Cory Booker’s campaign.
Tom Steyer
Read more about Tom Steyer’s campaign.
Mayor Pete Buttigieg
Read more about Pete Buttigieg’s campaign.
Sen. Kamala Harris
Read more about Kamala Harris’ campaign.
Sen. Bernie Sanders
Read more about Bernie Sanders’ campaign.
Sen. Elizabeth Warren
Read more about Elizabeth Warren’s campaign.
Former Vice President Joe Biden
Read more about Joe Biden’s campaign.
Read more:
The Democratic elite is panicked about Joe Biden, but he’s far more popular with voters than donors


I studied at Duke University, was a senior engineer at Microsoft, and have shipped several award winning games.
Outside work I enjoy reading from the Seattle Public Library, hiking in the Pacific Northwest, and trying out new recipes at home. Drop me a line and let's connect.


Document exchange
You can view and send documents to us.
Documents we've sent to you
View the documents we've sent to you.
Documents you've sent to us
View a list of documents you've sent to us.
Send a document
You can send documents to us.
You can view and send documents to us.
View the documents we've sent to you.
View a list of documents you've sent to us.
You can send documents to us.


Hello,
I've alreday have de thermal solar controler but I would like to relace it by a selfmade one with more option (collecting datas, web server, remote management).
But I need some extend to your project and I woud lilke if it is possible with the current controller, and not too much changement as I'm a newbie.
1 )My water tanks as 2 heater entries : one from se solar planed on from boiler.
So I would like to known if is possible to to add "Time controlled back-up heating ":
- a thermal sensor on the upper part of the water tank
- add an other relay to drive the pump of the boiler controlled by a Timer
2) Controll a Circulation pumps
- need a relay to active the pump
- need a timer (I jsut what th epump to turn on the morning)
- need a thermal sensor on return of the pump to know if it is usefull to activate it
3) Add a vacation mode
- when away for long time, the pumps is activate at night to cold the water tank
Re: Solar hot water controller - extensions
Concerning the Solar panel protection I don't know if it is already implement in the software, but the logic should looks like :
If the temperature in the storage tank reaches the set value 'Active from storage tank x°C' (something like “storage tank max” temperature – 7°C), Activate the Solar panel protection.
Then the solar circuit pump is switched off.
Since the solar circuit does not release heat via the storage tank, the solar panel temperature is automatically increased. When the set Solar Panel temperature ('SP') is exceeded, the solar circuit pump is switched on again and id possible (PWM ?) with low speed.
Only when the temperature on the Solar panel sensor has decreased by 10 Kelvin of the set value ('SP') is the solar circuit pump switch off again.
Re: Solar hot water controller - extensions
Hi Nitocris,
There is some documentation for a SHW controll and monitoring system up on this site: http://openenergymonitor.org/emon/solarhotwater. Hope this is useful.
Good luck wiht your project, Glyn.
Re: Solar hot water controller - extensions
My questions are precisely on any possible extension of this project.
Re: Solar hot water controller - extensions
Hello Nitocris
Yes you can extend it as much as you want, if your familiar with the arduino its just a matter of adding the sensors and actuators as you require for your project. My documentation covers what I needed but please by all means extend on this.
As for protection, I implemented both low temperature protection and high tempertature protection. If the water gets below 5 degrees C the pump turns on to stop the water from freezing. (5C just to be safe as cold wheather can still force it lower). If the tank is already at 80C plus then the pump will not turn on so that we prevent overheating or even boiling of the cylinder. I think I explained all this and provided software source code in the documentation like that Glyn provided so please have a look there.
Let me know how you get on with building it.
Trystan
Re: Solar hot water controller - extensions
Hi Guys,
With regards to the SHW http://openenergymonitor.org/emon/solarhotwater
I'd like to understand something, if it's controlling the pump, is the relay usually open or usually closed, I ask because say something happens the the emontx like it's power get's dropped, I would still like the pump to run, meaning that only when the water delivery is colder then the relay should break the current


Vala Parsian Tour Operator
The most professional and reliable private tour operator in IRAN
Persian Website(Not Available yet) English WebsiteThe most professional and reliable private tour operator in IRAN
Persian Website(Not Available yet) English Website


- get() - Method in class edu.cnm.deepdive.celestialbodies.controller.StarController
-
Returns a randomly selected
Star
resource, presumably for use in star information
retrieval-type applications.
- getAbsmag() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's absolute visual magnitude (its apparent magnitude from a distance of 10
parsecs).
- getAuth() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Gets authorization number regarding the user.
- getBase() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns a star in a multiple star system.
- getBayer() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the Bayer designation as a distinct value.
- getBf() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns The Bayer / Flamsteed designation, primarily from the Fifth Edition of the Yale Bright
Star Catalog.
- getCi() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's color index (blue magnitude - visual magnitude), where known.
- getComp() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns a star in a multiple star system.
- getComp_primary() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns a star in a multiple star system.
- getCon() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the standard constellation abbreviation
- getCreated() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Returns a timestamp when the user queried the database.
- getDec() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's declination, for epoch and equinox 2000.0.
- getDecrad() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the position in radians.
- getDist() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's distance in parsecs
- getFlam() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the Flamsteed number as a distinct value.
- getGl() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's ID in the third edition of the Gliese Catalog of Nearby Stars.
- getHd() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's ID in the Henry Draper catalog, if known.
- getHip() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns The star's ID in the Hipparcos catalog, if known.
- getHr() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's ID in the Harvard Revised catalog, which is the same as its number in the
Yale Bright Star Catalog.
- getId() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Returns autogenerated id for a user
- getId() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the database primary key
- getLum() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns a star's luminosity as a multiple of Solar luminosity.
- getMag() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's apparent visual magnitude.
- getPmdec() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's proper motion in declination.
- getPmdecrad() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the position in proper radians.
- getPmra() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's proper motion in right ascension
- getPmrarad() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the position in proper radians.
- getProfile() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Returns profile information about the user.
- getProper() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns a common name for the star.
- getRa() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's right ascension for epoch and equinox 2000.0.
- getRarad() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the position in radians
- getRating() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Gets the rating of a
Star
entered by the user.
- getRv() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's radial velocity in km/sec, where known.
- getSpect() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the star's spectral type, if known.
- getStars() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Returns
List
of queried stars.
- getVar() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns a star's standard variable star designation, when known.
- getVar_max() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns a star's approximate maximum magnitude.
- getVar_min() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns a star's approximate minimum magnitude.
- getVx() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the Cartesian velocity components of the star, in the same coordinate system described
immediately above.
- getVy() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the Cartesian velocity components of the star, in the same coordinate system described
immediately above.
- getVz() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the Cartesian velocity components of the star, in the same coordinate system described
immediately above.
- getX() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the Cartesian coordinates of the star, in a system based on the equatorial coordinates
as seen from Earth.
- getY() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the Cartesian coordinates of the star, in a system based on the equatorial coordinates
as seen from Earth.
- getZ() - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Returns the Cartesian coordinates of the star, in a system based on the equatorial coordinates
as seen from Earth.
- setAbsmag(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's absolute visual magnitude (its apparent magnitude from a distance of 10
parsecs).
- setAuth(Long) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Sets authorization number regarding the user.
- setBase(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets a star in a multiple star system.
- setBayer(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Bayer designation as a distinct value
- setBf(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Bayer / Flamsteed designation.
- setCi(Double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's color index (blue magnitude - visual magnitude), where known.
- setComp(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets a star in a multiple star system, comp = ID of companion star.
- setComp_primary(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets a star in a multiple star system.
- setCon(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the standard constellation abbreviation.
- setDec(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's declination, for epoch and equinox 2000.0.
- setDecrad(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the position in radians.
- setDist(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's distance in parsecs
- setFlam(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Flamsteed number as a distinct value.
- setGl(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's ID in the third edition of the Gliese Catalog of Nearby Stars.
- setHd(long) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's ID in the Henry Draper catalog, if known.
- setHip(long) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's ID in the Hipparcos catalog, if known.
- setHr(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's ID in the Harvard Revised catalog, which is the same as its number in the Yale
Bright Star Catalog.
- setId(long) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the database primary key
- setLum(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets a star's luminosity as a multiple of Solar luminosity.
- setMag(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's apparent visual magnitude.
- setPmdec(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's proper motion in declination.
- setPmdecrad(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the position in proper radians.
- setPmra(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets he star's proper motion in right ascension.
- setPmrarad(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the position in proper radians.
- setProfile(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Sets a String of user profile information.
- setProper(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the common name for the star.
- setRa(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's right ascension, for epoch and equinox 2000.0.
- setRarad(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the position in radians
- setRating(Long) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Gets the rating of a
Star
entered by the user.
- setRv(Double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's radial velocity in km/sec, where known.
- setSpect(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the star's spectral type, if known.
- setStars(List<Star>) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Operator
-
Sets a
List
of queried stars
- SetupData - Class in edu.cnm.deepdive.celestialbodies
-
This class pre-loads data from a csv file onto the Spring database.
- SetupData(StarRepository) - Constructor for class edu.cnm.deepdive.celestialbodies.SetupData
-
- setVar(String) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets a star's standard variable star designation, when known.
- setVar_max(Double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets a star's approximate maximum magnitude.
- setVar_min(Double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets a star's approximate maximum magnitude.
- setVx(Double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Cartesian velocity components of the star, in the same coordinate system described
immediately above.
- setVy(Double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Cartesian velocity components of the star, in the same coordinate system described
immediately above.
- setVz(Double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Cartesian velocity components of the star, in the same coordinate system described
immediately above.
- setX(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Cartesian coordinates of the star, in a system based on the equatorial coordinates as
seen from Earth.
- setY(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Cartesian coordinates of the star, in a system based on the equatorial coordinates as
seen from Earth.
- setZ(double) - Method in class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
Sets the Cartesian coordinates of the star, in a system based on the equatorial coordinates as
seen from Earth.
- Star - Class in edu.cnm.deepdive.celestialbodies.model.entity
-
Defines a database entity and REST resource representing the attributes of a star.
- Star() - Constructor for class edu.cnm.deepdive.celestialbodies.model.entity.Star
-
- StarController - Class in edu.cnm.deepdive.celestialbodies.controller
-
Defines REST endpoints for servicing requests on
Star
resources, invoking
StarRepository
methods to perform the required operations.
- StarController(StarRepository) - Constructor for class edu.cnm.deepdive.celestialbodies.controller.StarController
-
- StarRepository - Interface in edu.cnm.deepdive.celestialbodies.model.dao
-
Declares basic CRUD operations for
Star
instances in the server database,
using Spring Boot annotations.


Magic Fight
Create
Join
GitHub
Number of players (2-10):
Create
Back
Enter room id:
Join
Back
RoomID:
Waiting for other players ()
Leave the room


Nginx offers very proficient routing mechanisms in the form of location blocks. But since it is mostly used as a proxy server the location directives remain underutilized as simple endpoints for maybe passing on the requests to another webserver,rewriting urls or for redirecting http requests. Openresty turns nginx into a programable webserver and therefore gives us a better opportunity to fully utilize the capabilities of location block directives and create complex routing schemes for our applications.
In any webserver there are a few basic routing features that we take for granted:-
All of these features can be implemented quite easily in openresty with a mix of default nginx directives and some simple lua code.
Should hats have heads in them?
First let us see how we can parametrize the urls. Consider this simple location block
location ~*/a/(?<param>[a-zA-Z]+){
content_by_lua 'ngx.say("first match")';
}
The syntax it very simple to understand. `~*` at the beginning of the location string tells us to look for a case insensitive regex pattern. `(?<param>[a-zA-Z0-9]+)` captures a match in a named group called `param`. The match occurs if the character class [a-zA-Z0-9]+ is satisfied. If the url passes the regex in this location block you should see the string "first match" printed on the screen.
curl http://localhost:3125/a/b
first match
curl http://localhost:3125/a/baba
first match
Lets add another location block to make things a bit more interesting.
location ~*/(?<param>[a-zA-Z0-9]+)/a{
content_by_lua 'ngx.say("second match")';
}
The behaviour that we are expecting now is any url pattern that starts with 'a' should match the first location. And any url pattern that ends with "a" should match the second string.
curl http://localhost:3125/b/a/
second match
b
curl http://localhost:3125/a/b/
first match
b
curl http://localhost:3125/b/a/c
second match
b
As we can see the results are as expected. You can add rewrites to reject any string after the regex has been satisfied. For example the following rewrite in the first location block will not consider any string after the match
rewrite ~*/a/(?<param>[a-zA-Z0-9]+)/(.+) /a/$1/ permanent;
What this does is to rewrite all the requests of type /a/b/c to /a/b/ . So that nginx doesn't get carried away when matching regex locations.
Do walls ruin wallpapers?
Next let us look at limiting the methods only to a particular type of request. In nginx this is as easy as a thing can be.
location = /return_to_sender{
limit_except POST{}
return 200;
}
This location block only accepts post requests and denies everything else. Lets test it out:-
curl -v -X PUT http://localhost:3125/return_to_sender
< HTTP/1.1 405 Not Allowed
< Server: openresty
< Date: Sat, 08 Oct 2016 17:39:33 GMT
< Content-Type: text/html
< Content-Length: 399
< Connection: keep-alive
< ETag: "57f8c5e9-18f"
curl -v -X DELETE http://localhost:3125/return_to_sender
< HTTP/1.1 405 Not Allowed
< Server: openresty
< Date: Sat, 08 Oct 2016 17:41:19 GMT
< Content-Type: text/html
< Content-Length: 399
< Connection: keep-alive
< ETag: "57f8c5e9-18f"
<
curl -v -X POST http://localhost:3125/return_to_sender
< HTTP/1.1 200 OK
< Server: openresty
< Date: Sat, 08 Oct 2016 17:41:53 GMT
< Content-Type: text/plain
< Content-Length: 0
< Connection: keep-alive
< X-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
You can also supply more than one method to accept like so:-
location = /elvis{
limit_except POST PUT{}
return 200;
}
The location block above will only accept POST and PUT methods and deny everything else.
It is generally a good practice to explicitly specify what methods a location will except. Saves unwanted trouble later on.
Is bread better when eaten?
One of the features of modern routing frameworks is that it allows us to run functions before the processing is passed on to the actual handler. Openresty provides a good approximation for this in the form of access_by_lua handlers. In practice this is very simple. Consider that you want to to give an authenticated access to an endpoint. Here's a code snippet
location = /auth {
access_by_lua_block {
local headers = ngx.req.get_headers()
local uname_pass = 'abc:def'
if headers["Authorization"] ~= "Basic "..ngx.encode_base64(uname_pass) then
ngx.header["WWW-Authenticate"]="Basic"
ngx.req.clear_header("Authorization")
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
ngx.exit(ngx.OK)
}
return 200;
}
Any request to /auth without the valid authentication header will be rejected, requests with a valid auth header will be accepted. Now if you create a lua file out of this code you can simply reference the same file in as many location directives you want.This is not exactly a catch all function that most other frameworks provide but it's a pretty close enough approximation in practical terms.
Is cleanliness good for the soap?
There are certain principles that have served me well when creating routes for openresty applications:-
All code is available for testing on the github repository
-Akshat Jiwan Sharma


Figure 1.
FoodFab is a system that modifies a person's perceived satiety given a defined amount of calorie intake.
It accomplishes this by creating food structures of different chewing times, which can be accomplished by varying 3D printing parameters, such as infill pattern and infill density.
(a) Users input the type of food and level of hungriness, (b) FoodFab retrieves the required chewing time and matching infill parameters, and (c) 3D prints the food.
(d) All cookies have the same calorie amount, but different chewing times caused by variations in infill.
Food 3D printing enables the creation of customized food structures based on a person's individual needs.
In this paper, we explore the use of food 3D printing to create perceptual illusions for controlling the level of perceived satiety given a defined amount of calories.
We present FoodFab, a system that allows users to control their food intake through modifying a food's internal structure via two 3D printing parameters: infill pattern and infill density.
In two experiments with a total of 30 participants, we studied the effect of these parameters on users' chewing time that is known to affect people's feeling of satiety.
Our results show that we can indeed modify the chewing time by varying infill pattern and density, and thus control perceived satiety.
Based on the results, we propose two computational models and integrate them into a user interface that simplifies the creation of personalized food structures.
Over the last decades, 3D printers have started to move out of the realm of printing with plastic filament, resins, and metal powders and are now also being used for fabricating objects from edible materials. Today, 3D food printers are already in use in a variety of contexts from high-end restaurants that create new types of recipes to elderly care homes that use food 3D printing to reshape unappealing mashed food into more appealing shapes. 3D food printers are thus quickly becoming part of the ongoing revolution of the future of computing and food, and can make a difference in the way we eat, which is gaining more and more attention in light of global challenges around obesity and other eating related disorders.
Recently, food perception researches have started to investigate how to change peoples' perception of satiety by modifying different perceptual cues. Visual cues, for instance, play an important role: When a piece of food is cut into strips rather than cubes, people perceive it as more filling since it takes up more volume on the plate. Similarly, haptic cues, such as an increased biting force and the resulting longer chewing time affect people's feeling of satiety. For instance, people consume less food when eating hard food compared to soft food. Other features of food and food presentation, such as plate size and lighting, have been studied in cross-modal correspondence research, further extending our understanding of perceptual cues and illusions related to food intake.
Within HCI, we see a growing effort to implement such perceptual cues digitally by overlaying content onto the food using augmented reality (AR) and actuated devices. Narumi et al. showed that by visually overlaying a scaled image of food, seen through AR glasses, users perceive the food differently, i.e., believe the actual food changed in size. Similarly, haptic cues induced by vibration motors on the user's teeth and electrodes attached to the user's tongue allow to simulate different food textures and flavors. While these approaches deliver effective illusions, they require users to wear extra hardware and are less practical when used at home since users see the plain food before it is augmented. With the proliferation of food 3D printing technology, the creation of computationally-controlled food perceptions cues is no longer limited to the digital realm but can be tied back to the physical modification of food, which was originally used in food perception research. To investigate a first set of computationally controllable food 3D printing parameters, we hypothesize that changing the infill of a piece of food affects the chewing time, which has been shown to correlate with human satiety perception.
To evaluate the effect of infill, we setup a food 3D printer and fabricated pieces of food with varying infill patterns and infill densities. We tested our assumptions in two independent experiments with a total of 30 participants, in which we measured participant's chewing time using an electromyography sensor (EMS) and the perceived level of satiety using a self-report questionnaire. The results from both experiments show that changes in infill have a significant effect on chewing time and resulting perceived satiety.
Building on the data from the experiments, we develop two computational models, one for each 3D printing parameter (i.e., infill pattern and infill density). The models output the value of the printing parameter based on a person's desired increase in satiety. We then use these computational models to build an end-to-end system, called FoodFab, which simplifies the process of creating customized food structures (Figure 1).
In summary, our contribution is threefold: First, we use food 3D printing techniques to physically modify food, extending prior work on digital augmentation for creating perceptual illusions. Second, we run two experiments with food of varying infill patterns and infill densities to evaluate the effect of these printing parameters on the user's chewing time and perceived satiety. Third, we propose two computational models embedded in an end-to-end system that generate 3D printing parameters based on a desired increase in satiety.
We introduce FoodFab, a food 3D printing system that supports users in controlling their food intake. FoodFab induces varying levels of satiety by creating food structures of different chewing times but with the same amount of calories. To accomplish this, FoodFab modifies two 3D printing parameters: the infill pattern and the infill density. In the following sections, we present a detailed overview of the system structure and discuss the underlying fabrication techniques.
FoodFab System StructureAs can be seen in Figure 2a, FoodFab takes as input: (1) the type of food the user would like to consume (e.g. cookies) and (2) a rating of the user's current hungriness level (e.g.,~35/100). It also queries (3) the user's average daily calorie use from a fitness tracker (e.g., 2000 calories)-alternatively, this information can also be added manually to the user's profile).
Compute Allowable Calorie Intake for the Meal: FoodFab splits the amount of daily calories the user is allowed to consume over a set of daily meals (e.g., 30% for each main course: breakfast, lunch, dinner, and 5% for a morning and afternoon snack; values customizable in the UI). Based on the time of the day, FoodFab then retrieves the meal type and corresponding calorie percentage, and computes the allowable calorie for the meal for the particular user (Figure 2b). For our user, who is having a cookie as an afternoon snack and wants to reduce daily calorie intake by 200 calories, the allowable calories are (2000cal - 200cal) x 5% = 90 cal.
Compute Amount of Ingredient Given Allowable Calories: Next, FoodFab computes the amount of ingredient that can be used to stay within the allowable calories. Since cookie dough comes at 10cal/g and the user has 90cal available, the user can consume 9g (Figure 2c).
Scale the Food 3D Model to Match Ingredient Amount: FoodFab next scales the 3D model of the food to a size at which it requires only the allowed amount of ingredient (assuming solid infill in this processing step). In our case, in which the original cookie 3D model required 11g to print but the user can only consume 9g, FoodFab scales the model to 82% (Figure 2c).
Compute Necessary Increase in Satiety: Based on the user's hungriness level, FoodFab next computes the required increase in satiety. Assuming the user is currently slightly hungry (35% hungry) and would like to be full (0% hungry), the required increase in satiety is 35%.
Compute Chewing Time Required for Satiety Increase: Next, FoodFab takes the desired increase in satiety and retrieves the correlated chewing time from our computational models that we created based on the data from our experiments (Figure 2d). In our case, an increase in satiety of 35% correlates with a chewing time of 32 seconds.
Retrieve 3D Print Parameters for Required Chewing Time: Since a specific chewing time is achieved by a specific value of 3D printing parameter, FoodFab next uses our computational models to retrieve either an infill pattern or an infill density that causes the required amount of chewing for the given 3D~model. In our case, a chewing time of 32 seconds corresponds to an infill density of 61% (Figure 2e).
Apply 3D Printing Parameters to Food 3D Model: FoodFab next applies the 3D printing parameters to the 3D model that was previously scaled to match the allowable calorie intake and corresponding amount of ingredient. When decreasing infill density, the increased sparsity of the food's interior allows the model to be scaled up and to appear larger for the same amount of calories. As shown in Figure 2f, a 61% infill density, for instance, causes FoodFab to scale the 3D model by 3mm in x/y.
Slice and 3D Print: FoodFab then slices the 3D model and sends the resulting '.gcode' file to the 3D printer. The final food structure has a chewing time that leads to the desired increase in satiety while not exceeding the calorie amount previously specified.
Note that there are limits to how much we can modify satiety. As we will detail later in the experiments section, for a food of similar shape and cal/g, we can modify a user's perception of satiety within a range of $17-21%$ for infill pattern and 32-44% for infill density. Thus, taking infill density as an example and a hungriness level of 100%, users would have to eat 3.1 cookie with the least satiety inducing infill density but only 2.3 cookies for the most satiety inducing infill density (100% satiety = 3.1 cookies x 32% satiety increase vs. 2.3 cookies x 44% satiety increase). Given that one cookie in our experiments has 150 calories, this is a calorie saving of 70.4cal or 25.8% while resulting in the same user reported satiety level (70.4cal calorie saving = 272.80cal (3.1 cookies x 88cal) - 202.40cal (2.3 cookies x 88cal)). However, it is not possible to induce the same satiety level by consuming less calories than this given the user's choice of food.
The above five-step process illustrates the main principles underlying the FoodFab system and how it translates desired input and output parameters into the fabrication of food. Next, we describe our choice of 3D printing parameters in detail.
We chose to modify infill for two reasons: (1) variations in infill vary the mechanical strength of the printed food structures, which likely causes changes in chewing pattern and time, and (2) variations in infill are not visible from the outside.
Mechanical Strength: It is well known that infill affects the mechanical properties of 3D printed objects and thus in the case of 3D printed food structures may lead to different chewing patterns and chewing times and consequently different levels of perceived satiety. For instance, it has been shown that for the infill pattern parameter the `honeycomb' infill allows objects to withstand strong forces from all directions whereas the `rectilinear' infill pattern can only withstand forces from specific directions. Similarly, it has been shown that higher infill percentages correspond to higher mechanical strength of 3D printed objects.
Not Visible from the Outside: In addition, since varying infill parameters only affects the internal structure of the food, these structural variations are not visible when the food is viewed on the plate. Since the infill volume is enclosed by solid top and bottom layers, users cannot tell by looking at the food that the food differs inside.
Based on the desired properties of infill, we chose two different infill parameters for our studies.
Infill Pattern: Infill patterns, such as honeycomb, rectilinear, and Hilbert, change how the infill path is laid out (Figure 3). Since the path of each infill pattern requires approximately the same amount of ingredient, varying infill pattern does not affect the size of the input 3D model.
Infill Density: Infill density determines how sparse the model is on its interior (Figure 4). Since lower infill densities require less ingredient, the model can be enlarged to use the remaining ingredient for the rest of the geometry. This is especially desirable, since large food sizes have been shown to lead to an increase in perceived satiety.
We will next report on two experiments with 30 participants total to investigate the effect of infill parameters on chewing time and perceived satiety.
We conducted two independent experiments to investigate the influence of the two 3D printing parameters--infill pattern and infill density--on the chewing time and the user's perceived satiety. We first describe the 3D printing hardware and food ingredients used for the experiments, and then detail the experiment procedure and outcomes.
All food structures printed for the experiments were fabricated on the food 3D printer called 3DbyFlow that is shown in Figure 5. The 3DbyFlow printer allows accurate printing and fabrication of food with different internal structures, necessary to deploy different infill patterns and densities. Based on our experience with the 3D printer, food structures printed from the same 3D model (i.e., printed from the same ingredient amount) have an error rate of ±0.10g, which we determined by weighting the 3D printed food structure using a standard weight scale (SHIMADZU ELB300).
Using the 3DbyFlow printer, we tested different ingredients for creating 3D printed food structures. We focused on ingredients that have been previously used in food 3D printing research including cookie dough, avocado, pork puree, and ganache (see Figure 6).
Since our experiments rely on internal structures of a specific pattern and density, we found that ingredients with the following properties work best for our approach: (1) form a sturdy structure at room temperature, (2) if post-processing (cooking, baking) is required, the food keeps the same shape, size, and internal structure. The ratios were determined experimentally by repeatedly printing the ingredient and observing the resulting stability of the food structure.
Sturdy Structure at Room Temperature: We found that cookie dough, avocado, and pork puree result in a sturdy food structure at room temperature even when sparse infill is used. Ganache, however, was too liquid to maintain its structure even when prepared at room temperature (25C).
Post-Processing to maintain Shape, Size, and Internal Structure: We found that from all ingredients, 3D models printed from cookie dough best kept their shape, size, and internal structure after baking (average error from 3D model ±0.5mm). This was especially the case when the proportion of flour was raised. Pork puree had a larger variation after cooking. Although avocado did not require any post-processing, the phenols contained in avocado caused it to oxidize quickly, which reduced its visually appeal. Since reduced visual appeal may affect the experiment results, we removed it from the selection.
Overall, we found that cookie dough performed best from all ingredients. Using it as the experimental ingredient is also in line with prior work that used cookies as an experimental food item.
To prepare the cookie dough for 3D printing, we blended all ingredients until the cookie dough was evenly mixed. We then filled the dough into a syringe (NORDSON EFD 7012134, 30cc Syringe Barrels) and removed air bubbles resulting from the transfer process by placing the dough inside a vacuum chamber (BACOENG 3 Gallon Vacuum Chamber Kit) for 5 minutes. Our 3D printing nozzle for printing the cookie dough has a diameter of 1.2mm (NORDSON 7018100 Tips; 16GA TT .047" Grey). We had previously experimented with different nozzle sizes 0.8 to 1.6mm and found that nozzles larger than 1.2mm created large printing error while smaller nozzles did not properly extrude the dough.
Before printing, we attached baking paper to the top of the printer glass plate (see Figure 5), which we use later to move the printed cookie from the printer to the oven.
For the experiments, we printed square shaped cookies with either different infill patterns (experiment 1) or densities (experiment 2). All cookies were baked in the oven at 150C-180C, with top and bottom heat, for 15-30 minutes, depending on the size of cookie.
We measured the size and weight of each cookie after baking using a standard weight scale (SHIMADZU ELB300) to verify that cookies had the expected weight and size (within an error of ±0.10g and ±1.00mm). Baked cookies with larger error were removed prior to the experiment.
In both experiments users ate different types of cookies that varied along the parameter space of the 3D printing parameter. We measured users' chewing time and perceived satiety using a combination of EMG and questionnaires.
Measuring Chewing Time (EMG): To measure chewing time, we used an electromyography (EMG) sensor (Tokyo Devices IWS940-DEV) attached to the masticatory muscle of the user's jaw (Figure 7). EMG allows for more accurate measurements than having participants manually start/stop a timer and also has the added benefit that it keeps users' hands free for eating. We later use the data points from the chewing time as input for the computational models that compute the 3D printing parameters.
Measuring Perceived Satiety (Questionnaire): To measure user's perceived satiety, we adapted the questionnaire used in prior work and used the questions: ''How hungry are you now?'' and ''How full are you now?''. We used the magnitude estimation method in which participants rate their hunger and fullness between 0% ('not hungry at all' or 'not full at all') to 100% ('extremely hungry' or `extremely full'). Participants' ratings were recorded using an on-screen questionnaire to investigate the correlation between chewing time and perceived satiety (combining objective and subjective measures). The questionnaire was completed before and after eating cookie.
The experiments were conducted in a quiet, private room at a set light illumination of 500 lux. In the middle of the room, we setup a table on which we placed the cookies, a cup of water, and a computer for answering the questionnaire. Participants were asked to sit comfortable on a chair at the table.
In the following section, we provide details for each of the two experiments. For both experiments, ethics approval from the Local University Ethics committee was obtained, and written consent collected from participants.
In the first experiment, we fabricated cookies with different types of infill patterns to investigate their effect on users' chewing time and perceived satiety.
The experiment followed a within-subjects design with three experimental conditions, i.e., three cookie types with varying infill patterns and with the same physical weight (15g, ~0.10g error) and thus the same calories (1g cookie dough = 10cal, 150cal $\pm$1cal). Figure 3 shows an overview of all three cookies and Table~\ref{tab:table-experiment-1} summarizes their respective measurements and parameters. Cookie #1 was printed with the honeycomb infill pattern, cookie #2 with the Hilbert infill pattern, and cookie #3 with the rectilinear infill pattern, all at 25% infill. Each cookie across all types had a fixed height of 5mm, which it equal to 7 printed layers in our setup. In addition, we gave participants a solid reference cookie as one would obtain from traditional cooking to provide a normal reference representing common experience. The reference cookie was of the same size but had a solid infill (100%) and thus a different weight (22.45g) and calorie amount (224.50cal). The data for the reference cookies is included in the supplementary material.
Based on the mechanical properties of cookie #1-#3, we hypothesized that the honeycomb infill pattern creates the longest chewing time and highest perceived satiety because honeycomb creates the most robust structure, followed by the Hilbert pattern and rectilinear pattern last. The reference cookie cannot be included in the comparison since it is made from a larger ingredient amount and thus contains more calories.
ProcedureParticipants took part in a total of four sessions, each lasting an average of 10 minutes from pre- to post-questionnaire. Each session took place on a different day to reduce effects from overly long chewing time. In each session participants were presented with one of the four types of cookies (one of the infill patterns or the solid reference cookie). We randomized the order of the cookie types between participants using the Latin Square design to avoid order bias.
Before each session, participants were asked to fill out the questionnaire to measure their perceived satiety. Then, participants were presented with 10 cookies of the specific type they were assigned to for the session. Participants were asked to eat as many of the 10 cookies as they wanted. After eating each cookie, we asked them to rate their perceived satiety again using the same questionnaire.
ParticipantsWe recruited ten unpaid volunteers (7 males and 3 females) from a local university to participate in the experiment. Before signing up as participants, they had to confirm that they had no food allergies or restrictions. Participants were instructed not to eat for at least four hours before taking part in the experiment in order to ensure that they had enough appetite. Participants were allowed to drink water after each cookie but not while eating the cookie to prevent any effect the water intake may cause.
Chewing Time: Figure 9a shows the average chewing time for eating one cookie in each of the three different conditions. All participants finished 10 cookies without leftovers. We found that the longest chewing time was elicited by cookie #1 (Honeycomb, 84.2s), followed by cookie #2 (Hilbert, 72.2s) and cookie #3 (Rectilinear, 61.6s). We applied a one-way analysis of variance (ANOVA) and found a significant effect of infill pattern F(4, 29)=4.75, p<0.05). The post-hoc analysis using Bonferroni showed a significant difference between cookie #1 and cookie #2, cookie #2 and cookie #3 (p<0.05), and cookie #1 and cookie #3 (p<0.01). Figure 8 shows the measured chewing time in each condition.
Perceived Satiety: Figure 9b shows the average increase in perceived satiety, i.e. the difference in how hungry participants rated themselves via the pre- and post-questionnaires before and after eating each cookie in each condition. The questionnaire results indicate that participants felt less hungry after eating cookie #1 (21.30% average increase in satiety), compared to cookie #2 (19.51% average increase in satiety) and cookie #3 (17.13% average increase in satiety). We applied a one-way ANOVA to the questionnaire results (i.e., average score between pre- and post-questionnaire). However, we did not find any significant effect of perceived satiety among the conditions (F(4, 29)=7.35, p=0.79).
Based on the results, we conclude that we can indeed control chewing time and perceived satiety by varying the infill pattern. While we cannot find a significant difference of perceived satiety among the infill patterns, the results indicate a different average among all conditions.
Following the same procedure as in experiment 1, we de-signed a second experiment to investigate the effect of different infill densities on chewing time and perceived satiety. Since we had to choose one infill pattern to be able to control this variable, we selected `honeycomb'. For future work additional experiments are needed to complement the information of the other infill patterns with their respective infill densities as well.
The experiment followed the same design with three experimental conditions, however, this time the three cookie types had varying infill densities.
As mentioned earlier, printing cookies using the same amount of ingredient (to keep the calories the same) but varying the infill density results in a change of cookie sizes. Figure 3 shows the three printed cookie types.
Cookie #1 was printed with 39% infill (40x40mm), cookie #2 with the 55% infill (35x35mm) and cookie #3 with 70% infill (25x25mm). Each cookie had a fixed height of 5mm, which is equal to 7 printed layers in our setup. In addition, participants were given the solid reference cookie again in one of the four sessions.
We recruited twenty unpaid volunteers (14 males and 6 females). Out of the twenty participants, ten had participated in experiment 1 but we did not observe any difference in their data when compared to the other participants.
Chewing Time: Figure 10a shows the average chewing time for eating one cookie in each of the three conditions. All participants finished 10 cookies without leftovers. We found the longest chewing time was elicited by cookie #1 (lowest infill / largest size, 45.30s), followed by cookie #2 (medium infill / medium size, 36.35s), and cookie #3 (largest infill / smallest size, 26.0s). We applied a one-way ANOVA and found a significant effect of infill density (F(4, 29) = 22.10, p <0.01). The post-hoc analysis using Bonferroni showed a significant difference among different infill densities (p < 0.05) except between cookie #1 and cookie #2.
Perceived Satiety: Figure 10b shows the average increase in perceived satiety, i.e. the difference in how hungry participants rated themselves via the pre- and post-questionnaires before and after eating each cookie in each condition. The questionnaire results indicate that participants felt least hungry after eating cookie #1 (lowest infill / largest size, 43.8% average increase in satiety), followed by cookie #2 (medium infill / medium size, 38% average increase in satiety) and cookie #3 (largest infill / smallest size, 31.75% average increase in satiety). We applied a one-way ANOVA to the questionnaire results (i.e., average score between pre- and post-questionnaire) and found a significant effect of infill density on perceived satiety (F(4, 29)} = 17.086, p< 0.01). The post-hoc analysis using Bonferroni showed a significant difference among each pair of different infill densities (p< 0.05).
Thus, given these results, we conclude that we can indeed modify chewing time and perceived satiety by varying the infill density.
Based on the results from the two experiments, we conclude that we can modify the chewing time by modifying a food's internal structure via 3D printing parameters, such as infill pattern and infill density, and that the chewing time is correlated with the perceived satiety.
To make our experimental results applicable in the context of creating food structures of a desired chewing time, we create two computational models, one for each of the 3D printing parameters infill pattern and infill density.
Each model consists of two parts: (1) a mapping of perceived satiety to chewing time, and (2) a mapping of chewing time to 3D printing parameter, i.e. a specific infill pattern type or infill density percentage. Note that these computational models are meant as an illustration of the potential applications that FoodFab will enable, i.e. they only work for a food 3D model of the same initial dimensions as used in the experiment.
Mapping Perceived Satiety to Chewing Time: Prior work has shown that chewing time has a correlation with perceived satiety, which was also confirmed by our experiments (i.e., the longer participants chewed, the higher they rated their perceived satiety afterwards, with significant effects for infill density and correlated increased averages for infill pattern).
To show this effect in one graph, we summarize the data on chewing time and perceived satiety in Figure 11a with perceived satiety on one axis and chewing time on the other. We then fit a linear graph resulting in the following formula: chewing time t = 0.18 x perceived satiety increase + 5.96. Thus, once users indicated their hungriness level (i.e., current perceived satiety level), we can determine the required increase in satiety, and then compute the necessary chewing time. For example, an increase in satiety of 20% requires a chewing time of 76s when infill pattern variation is used (76s = 0.18 x 20% + 5.96).
Chewing Time to Infill Pattern Type: Next, we can use the required chewing time to select an infill pattern that creates this chewing time from Figure 11b. Since infill pattern is a discrete value, we discretize the continuous space while minimizing error. Continuing the previous example, if the user needs to chew 76s, the hilbert infill pattern matches the chewing time most closely.
Maximum Increase in Satiety and Chewing Time: As can be seen in Figure 11a/b, using infill pattern we can modify a user's perception of satiety within a range of 17-22% and the chewing time from 61 seconds to 84 seconds.
Perceived Satiety to Chewing Time: Similarly, we can summarize the data for infill density as shown in Figure 12a with perceived satiety on one axis and chewing time on the other. We then fit a linear graph resulting in the following formula: chewing time t = 0.62 x perceived satiety increase + 15.47. Thus, once users indicated their hungriness level (current perceived satiety), we can calculate the required increase in satiety and then compute the required chewing time. For instance, as mentioned in Figure 2d, a desired increase in satiety of 35% requires a chewing time of 37.17s when infill density variation is used (37.17s = 0.62 x 35 + 15.47).
Chewing Time to Infill Density Percentage: Next, we retrieve the infill density that creates the required chewing time (Figure~ 12b). To create a formula that computes the matching infill density, we applied regression estimation using a linear function (R2 = 0.793, p <0.01) that interpolates between the different densities. As result, we obtain the following equation: T{chew}= 0.62 x D{infill} + 69.88 where T{chew} represents the chewing time and D{infill} represents the infill density. Entering 32s for chewing time into this equation, results in an infill density of 61% (Figure 2e).
Scaling the Model: After determining the desired infill density, we can scale up the model to enlarge the surface area. Given our predefined shapes (square cookies), we obtain the following formula: Area = -0.39 x D{infill} + 55.27)^2. Thus, given the infill density of 61%, our model will be scaled by a factor of 0.95 (area for infill density 100% = 30mm^{2}, area for infill density 61% = 31.48mm^{2}, 30mm^{2}/31.48mm^{2} = 0.95). This is also shown in Figure 2f.
Maximum Increase in Satiety and Chewing Time: As can be seen in Figure 12a/b, using infill density we can modify a user's perception of satiety within a range of 31-45% and the chewing time from 26 seconds to 46 seconds.
Our computational models allow us to create the user inter-face that is conceptually shown in Figure 2 and implemented as a plugin to the 3D editor Rhino as shown in Figure 13.
As explained at the beginning of the paper, it takes as input a user's selection from a list of foods, a level of hunger, and the daily calorie use. Note that, the daily calories use is either automatically retrieved from a user's fitness tracker or predefined in the user's profile. The system then outputs a set of 3D printing parameters and ''.gcode'' file for 3D printing that create the required chewing time.
Throughout this paper, we contributed a first exploration of how food 3D printing can be used to physically modify food. Thus, we extend prior work that digitally augmented food and, most notably, remove the need for wearing attachments by using 3D printers to computationally-control different parameters concerning the food structure. In the next section, we discuss extensions of our work for food perception research, detail current limitations of our approach, and outline opportunities for future work.
In this paper, we focused on two specific 3D printing parameters, i.e., infill patterns and infill density, and showed that food 3D printing cannot only be used to create novel types of taste structures as was the focus of prior work, but also has important implications for modifying food intake. As mentioned previously, food 3D printing modifies the food structures physically, which is closer to the process originally used in food perception research. Thus, by building onto our work that explored a first set of printing parameters and demonstrated how to embed the results into an end-to-end food fabrication pipeline, food perception researcher can add to the space of available perceptual illusions, further enhancing the parameter space for computer-controlled food fabrication. In addition, by automating the generation of computationally modified food, our work facilitates follow up research on cross-modal cues, such as effects that are caused by grasping or seeing the food. Further research is needed to integrate different cues into one such coherent model.
In our work, the experiments were run on one specific type of food and thus the resulting data points result in computational models that can only be used for food structures of the same characteristics. Further work across additional ingredients and food shapes is needed before the results can be generalized for larger classes of food types. In addition, our work only investigated two printing parameters, i.e., infill patterns and infill density, but other parameters, such as the number of shells and number of top and bottom layers may also influence the chewing time. Furthermore, while the food structures in this paper were chosen to maintain their shape during printing even when infill is varied, it is more difficult to vary infill when printing structures have steep overhangs or are large in size. Finally, while we hope that our work may help in reducing overeating, additional experiments are needed to evaluate if people who overeat can reliably rate their own satiety levels.
While food 3D printing is still a new research topic, the ex-tended interest in food computing and a first deployment and use of food 3D printers in restaurants point towards a future in which people will make use of such personalized automated meals. Below, we describe three usage scenarios where our approach can be useful.
Integration with Daily Meal Preparation: Food 3D printers that emerge on the consumer market tend to offer a list of recipes linked on the manufacturer website where a user can buy the ingredients as prefilled food capsules in supermarkets. FoodFab can be integrated into this emerging ecosystem around food 3D printers by using computational models to personalize the recipes offered to consumers to help them manage their food intake.
Decreasing Food Size Over Time: People who overeat tend to have an attraction to large portion sizes to which they get accustomed over time. Thus, to a person who overeats, a regularly sized meal may seem small in comparison. A future avenue for FoodFab would be to investigate if small changes in meal size over time can get users accustomed to regularly sized portions. As we have previously shown, we can modify the size of a piece of food by changing the infill density while keeping calories constant.
Culinary chefs: We envision that in restaurants, Chefs will not only customize a user's meal based on allergies and other food restrictions, but will take calorie constraints into account as well. By being able to modify the food's size without adding calories, Chefs will be able to prepare meals of equal sized portions for people that are sharing a dinner experience in a group. Note that we see our work in line with the Digital Gastronomy paradigm, in which the chef and the fabrication device work hand-in-hand in the creative process of preparing a meal, i.e. the chef is not replaced by a machine but the manual practice of the chef is enhanced with digital capabilities.
Extending to other Fabrication Methods: Finally, our idea of realizing food perception tricks goes beyond 3D printing and can also be applied to other fabrication processes. Figure 14 shows how we use laser cutting to chop food to increase the apparent size. Compared to 3D printing, laser cutting has the benefit that it works with ingredients other than pastes. For instance, laser cutters are able to process materials, such as crackers and ham. However, on the flip side, laser cutting is limited in that it cannot cut through thick shapes and due to the heat employed can burn or melt a piece of food.
In this paper, we demonstrated how to use personal fabrication devices to create food perception illusions. Rather than digitally augmenting food, we showed how to use food 3D printing to physically integrate perceptual cues.
We investigated how by changing infill parameters of a piece of food, we were able to modify the chewing time and correlated perceived satiety while maintaining the same amount of ingredient and thus calories. We incorporate the results from our experiments into two computational models, which estimate the 3D printing parameters for a required chewing time and desired satiety level. For future work, we plan to collaborate with food perception researchers to evaluate the effectiveness of structural and composition changes and to study long-term habituation effects, such as those outlined by Velasco et al.
We would like to thank Isabel Qamar for her help with the video narration. This work has been supported by the JSPS KAKENHI under Grant No. JP15H05925 and JP19K20321, and the ERC under the European Union's Horizon 2020 research and innovation program under Grant No. 638605.


PageSpeed Admin Pages
The admin pages are a collection of features that provide visibility into the operation of the PageSpeed optimizations.
The pagespeed_admin and pagespeed_global_admin pages aggregate a set of pages showing server state so they can be accessed from a single handler. By organizing all these features under a single admin page, this can be done once, and can serve as a launching point for future administration features. Before version <IP_ADDRESS> the admin pages were read-only, but starting in version <IP_ADDRESS>, cache purging is supported.
The name of the currently active page is underlined in the top navigation bar.
| Page | Related Options | Description |
|---|---|---|
| Statistics |
Statistics UsePerVHostStatistics mod_pagespeed_beacon ngx_pagespeed_beacon
|
Shows server statistics since startup, such as how many resources are being optimized by filters, as well as various latency and cache effectiveness metrics. |
| Configuration | Configuring FiltersModPagespeedIf (Apache only) |
Shows detailed configuration information including all filters, options, and the current cache flush timestamp. |
| Histograms |
add_instrumentation |
Shows detailed latency data for Page Load Time, rewriting, caches and HTTP fetching. |
| Caches |
MemcachedServers
CreateSharedMemoryMetadataCache
EnableCachePurge
|
Shows detailed cache configuration information. When accessed from the Admin handler, it can be used to inspect the contents of the cache, and provides an interface to purge the cache. |
| Console |
Statistics StatisticsLogging LogDir
|
Displays a console of graphs of server optimization behavior over time. |
| Message History |
MessageBufferSize
|
Server-wide history of recent logging output from PageSpeed, including messages that are omitted from the server log file based on its log level. |
Before <IP_ADDRESS>, the main admin page is not available, but there
are page-specific handlers for statistics, messages, and the
console. In <IP_ADDRESS> and later, the *_pagespeed_*
handlers, such
as mod_pagespeed_statistics
, will continue to be supported:
- They provide read-only access to server operation. There may be cases where a site owner wants to share statistics or console information but not the ability to purge the cache.
- Existing configurations must continue to work after an upgrade to a release that supports pagespeed_admin.
- The admin pages may later gain support for modifying the server state
Configuring Admin Pages
In this table we use the term "server" for an Apache VirtualHost and an nginx Server Block. We use the term "global" to mean the entire Apache or nginx system, covering all the configured VirtualHost and Server Blocks.
| Apache Handler | Nginx Option | Version | Description |
|---|---|---|---|
pagespeed_admin |
AdminPath |
<IP_ADDRESS>+ | Covers all administrative functions for a host in one handler. If you establish this handler, you don't need any of the other server-scoped methods. Only give 'admin' page access to clients that you are comfortable allowing to modify the state of your PageSpeed configuration. |
pagespeed_global_admin |
GlobalAdminPath |
<IP_ADDRESS>+ | Covers all administrative functions for
the entire global state in one handler. If you establish this
handler, you don't
need mod_pagespeed_global_statistics . |
mod_pagespeed_statistics |
StatisticsPath (<IP_ADDRESS>+) |
All | Launchpad for Statistics, Histograms, and a subset of the Caches page as described above. |
mod_pagespeed_global_statistics |
GlobalStatisticsPath (<IP_ADDRESS>+) |
1.1+ | Same as above, but aggregates statistics across all
configured servers. You must enable
UsePerVHostStatistics for separate global
statistics to be retained, otherwise all statistics will be global. |
mod_pagespeed_message |
MessagesPath (<IP_ADDRESS>+) |
1.0+ | Displays recent log messages printed by PageSpeed,
including messages that may be below the current server loglevel
threshold such as "Info" messages. Requires that
MessageBufferSize be set. |
pagespeed_console |
ConsolePath (<IP_ADDRESS>+) |
1.6+ | Displays a console of graphs of server optimization behavior over time. |
Establishing Handlers in Apache
Each handler is optional; add them individually to enable
admin features. Note that when you add handlers for
pagespeed_admin
and pagespeed_global_admin
you are granting read/write access to server-state. The other handlers
are read-only. A sample handler that filters on IP address is
in the default configuration, whose general form is:
<Location /PATH> Order allow,deny Allow from localhost Allow from <IP_ADDRESS> SetHandler HANDLER_NAME </Location>
You can choose any path for a handler, but you must specify the handler
name exactly as it appears in the table above. By convention we use
use the handler name for the path. You may also want to
employ login-based access to the admin pages, using
AllowOverride AuthConfig
. Please see the Apache
2.2
or
2.4
Documentation for details.
Establishing Handlers in Nginx
In nginx, the handlers must be specified as location blocks.
location /ngx_pagespeed_statistics { allow <IP_ADDRESS>; deny all; } location /ngx_pagespeed_global_statistics { allow <IP_ADDRESS>; deny all; } location /ngx_pagespeed_message { allow <IP_ADDRESS>; deny all; } location /pagespeed_console { allow <IP_ADDRESS>; deny all; } location ~ ^/pagespeed_admin { allow <IP_ADDRESS>; deny all; } location ~ ^/pagespeed_global_admin { allow <IP_ADDRESS>; deny all; }
Note that these handlers must precede the
"\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+
" location block.
In version <IP_ADDRESS> and later, the above location blocks are needed for each path you elect to enable in PageSpeed options:
pagespeed StatisticsPath /ngx_pagespeed_statistics; pagespeed GlobalStatisticsPath /ngx_pagespeed_global_statistics; pagespeed MessagesPath /ngx_pagespeed_message; pagespeed ConsolePath /pagespeed_console; pagespeed AdminPath /pagespeed_admin; pagespeed GlobalAdminPath /pagespeed_global_admin;
You can choose any path, as long as it's consistent between
the pagespeed Path
and the location
. By
convention we use the names as specified in the example.
Prior to version <IP_ADDRESS>, the above "Path" settings do not exist,
and the failure to specify location blocks leaves the paths active
with no access restrictions. The module will service requests
to the paths whether the location blocks are specified or not.
This applies to /ngx_pagespeed_statistics
,
/ngx_pagespeed_global_statistics
,
/ngx_pagespeed_message
, and /pagespeed_console
.
If you define access control for /pagespeed_admin
or
/pagespeed_console
, you must do so earlier in the configuration
file than the path to handle .pagespeed
resources, to ensure
that the handlers are disambiguated.
Limiting Handler Access
Note: New feature as of <IP_ADDRESS>
Apache's SetHandler access controls are accessible to anyone who can
modify .htaccess
files, so in a typical shared hosting context
the global admin site isn't sufficiently protected. As of <IP_ADDRESS>,
PageSpeed allows setting an additional restriction of what domains are allowed
to load handlers. For example, to deny access entirely, you could put:
- Apache:
ModPagespeedStatisticsDomains Disallow * ModPagespeedGlobalStatisticsDomains Disallow * ModPagespeedMessagesDomains Disallow * ModPagespeedConsoleDomains Disallow * ModPagespeedAdminDomains Disallow * ModPagespeedGlobalAdminDomains Disallow *
- Nginx:
pagespeed StatisticsDomains Disallow *; pagespeed GlobalStatisticsDomains Disallow *; pagespeed MessagesDomains Disallow *; pagespeed ConsoleDomains Disallow *; pagespeed AdminDomains Disallow *; pagespeed GlobalAdminDomains Disallow *;
To allow access only to an admin, define a new VHost
like admin.example.com
, use standard web-server access control
(IP or password) to restrict access to only that admin, and then at the top
level of your config put:
- Apache:
ModPagespeedStatisticsDomains Allow admin.example.com ModPagespeedGlobalStatisticsDomains Allow admin.example.com ModPagespeedMessagesDomains Allow admin.example.com ModPagespeedConsoleDomains Allow admin.example.com ModPagespeedAdminDomains Allow admin.example.com ModPagespeedGlobalAdminDomains Allow admin.example.com
- Nginx:
pagespeed StatisticsDomains Allow admin.example.com; pagespeed GlobalStatisticsDomains Allow admin.example.com; pagespeed MessagesDomains Allow admin.example.com; pagespeed ConsoleDomains Allow admin.example.com; pagespeed AdminDomains Allow admin.example.com; pagespeed GlobalAdminDomains Allow admin.example.com;
Now when you visit admin.example.com/pagespeed_global_admin
you'll see global (server-level) admin information, but users are not able to
access this under their own domain or turn the handler on
with .htaccess
.
For all six of these options the default value is Allow *
. If
you explicitly Allow
access to any site, all others are
automatically Disallow
ed. Wildcards are allowed, and additional
directives are applied in sequence. For example, consider the following
config:
- Apache:
ModPagespeedAdminDomains Allow *.example.* ModPagespeedAdminDomains Disallow *.example.org ModPagespeedAdminDomains Allow www.example.org
- Nginx:
pagespeed AdminDomains Allow *.example.*; pagespeed AdminDomains Disallow *.example.org; pagespeed AdminDomains Allow www.example.org;
This would allow access to www.example.com/pagespeed_admin
,
and www.example.org/pagespeed_admin
but
not shared.example.com/pagespeed_admin
.
Shared Memory Statistics
By default PageSpeed collects cross-process statistics. While
they're mostly intended for debugging and evaluation
using /mod_pagespeed_statistics
, /ngx_pagespeed_statistics
,
and the PageSpeed Console, statistics are also
necessary for limiting concurrent image rewrites
and background fetches.
It's not recommended to turn them off, as their performance impact
is minimal, but if you need to you can do so with:
- Apache:
ModPagespeedStatistics off
- Nginx:
pagespeed Statistics off;
Virtual hosts and statistics
You can choose whether PageSpeed aggregates its statistics
over all virtual hosts (the default), or to keeps separate counts for each. You
can chose the latter by specifying
UsePerVHostStatistics on
. In that
case, /pagespeed_admin
, /mod_pagespeed_statistics
and /ngx_pagespeed_statistics
will show the data for
whatever virtual host is being accessed. If you do turn per-virtual
host statistics on, you can still access the aggregates
under /pagespeed_global_admin
, /mod_pagespeed_global_statistics
or /ngx_pagespeed_global_statistics
.
- Apache:
ModPagespeedUsePerVhostStatistics on
- Nginx:
pagespeed UsePerVhostStatistics on;
Message Buffer Size
Determines the number of bytes of shared memory to allocate as a circular buffer for holding recent PageSpeed log messages. By default, the size of this buffer is zero, and no messages will be retained.
- Apache:
ModPagespeedMessageBufferSize 100000
- Nginx:
pagespeed MessageBufferSize 100000;