https://www.linkedin.com/posts/philipphasper_vuforia-ai-ar-activity-7272260967709773826-fSgI
Can’t open a Microsoft Outlook protected message? This is how you work around it
Email encryption forever is a pain point in the IT ecosystem. PGP is a great system but hasn’t been widely adopted.
This is how I personally have used PGP in the past, for the <10 PGP emails I have received over my entire lifetime:
Microsoft has rolled out their own solution to the problem: Outlook Protected Messages. A proprietary system on top of an open, wide-spread standard – I don’t really like that but hey, it is better than nothing!
If an Outlook user sends you such a protected email to a non-Outlook and non-GMail address, you will receive an email “Alice has sent you a message that was protected with Microsoft Office 365” and a link to click. You’ll be redirected to a page where you can sign in and receive a single-use code sent to your email address.
But here is the catch: This sign in just doesn’t work! The email I received these messages is not connected to a Microsoft account. So I could not login to request the single-use code. I then tried it with an email which was connected to a Microsoft account – turns out, this also doesn’t work! Even if the protected email was sent to this Microsoft account.
Somehow, the solution to this is to trick Microsoft into your browser being a mobile browser. Then, you are not asked for any sign in but can directly request the single-use code. This is how you do it, using the Browser’s developer tools:
- Copy the link from the protected message
- Open a new empty browser tab.
- Right-click → Inspect
- Click the “device icon”
- On Firefox, it is on the right side of the bar
- On Chrome, it is on the left side of the bar
- Your browser now acts as a mobile browser.
- Enter the link into the address bar
- Request the single-use code to be sent to your email
- Then enter the received code in the browser
Privacy in the Metaverse
Or: How to install any app on the Quest 3 without giving Meta your phone number.
With the long anticipated Apple Vision Pro become available at February 2nd 2024 (unfortunately only in the US), we’ll finally see Apple’s take on a consumer-ready headset for mixed reality – er … I meant to say spatial computing. Seamless video see-through and hand tracking – what a technological marvel.
As of now, the closest alternative to the Vision Pro, those unwilling to spend $3500 or located outside the US, seems to be the Meta Quest 3. And this only at a fraction of the price, at $500. But unlike Apple, Meta is less known for privacy-aware products. After all, it is their core business model to not be.
This post explains how to increase your privacy on the Quest 3, in four easy steps.
Continue reading Privacy in the MetaverseEine Tour durch C++
English tl;dr: I helped with the German translation of Bjarne Stroustrup’s “A Tour of C++”. As it is about a German book, this post is in German as well
Nachdem Sie mit dem Buch “C++ Schnelleinstieg” einen praktikablen Einstieg in diese Sprache gewagt haben, können Sie als nächstes Ihr Wissen mit “Eine Tour durch C++” vervollständigen. Bei der Übersetzung dieses Buches vom Englischen ins Deutsche habe ich als Fachkorrektor mitgewirkt.
Dieses Buch deckt zahlreiche Features ab, die mit C++20 neu eingeführt wurden, darunter Module, Konzepte, Koroutinen und Bereiche. Selbst einige schon jetzt verfügbare Komponenten, die nicht vor C++23 in den Standard integriert werden sollen, werden vorgestellt.
Beim Verlag kaufen: Eine Tour durch C++
Über den Autor:
Bjarne Stroustrup hat C++ entwickelt und die erste Implementierung geschrieben. Derzeit ist er Professor an der Columbia University und hat zuvor bei AT&T Bell Labs an der Texas A&M University sowie bei Morgan Stanley gearbeitet.
Step Check Open Beta start
Wow, what a journey 🚀😎 . We are so proud to finally publicly share one of the things we have worked on since ioxp joined Vuforia: The new Vuforia Instruct feature Step Check™, starts its Public Beta now!
Based on the best-in-class Vuforia technology and developed together with many of the brightest people I know:
✴️Assisted quality assurance using #augmentedreality
✴️Integrated AI training
✴️Authoring from CAD
Next steps: learn from the beta feedback, improve and deliver!
Thank you, Vuforia, for how all of you welcomed us and for how we made things possible – together!
#ar #computervision #mixedreality #innovation #service #qualityassurance #acquisition
Update: Special video for the third Sunday of Advent 😉
C++ Weihnachtsspecial
English summary: This is a holiday special for my German C++ beginners book, hence this post is in German.
Dieser Blogpost erweitert Ihr Wissen aus dem Buch C++ Schnelleinstieg: Programmieren lernen in 14 Tagen. Nach dem Kapitel 9 (Fortgeschrittene Konzepte) haben Sie alle nötigen Grundlagen, die Sie als Voraussetzung für das Special benötigen:
- Grundlagen der Objektorientierung
- Bibliotheken mittels vcpkg einbinden
- GUI-Programmierung mit Nana
- Callbacks und Lambdas
- Fehler werfen
In diesem Projekt soll ein Weihnachtsbaum programmiert werden, bei dem Sie die Kerzen per Mausklick anzünden können. So sieht das ganze aus, wenn es fertig ist:
Das Codebeispiel beginnt in der main
-Funktion des Programms und fügt nach und nach zusätzliche Komponenten oberhalb ein – daher beginnt die Zeilennummerierung so wie sie in der fertigen Datei sein wird (Downloadlink am Ende des Beitrags). Der Code wird durch Erklärungen unterbrochen, wie Sie an den fortlaufenden Zeilennummern erkennen können.
int main()
{
nana::paint::image tree("tree.png");
if (tree.empty())
{
/* Sie müssen die drei Bilder (tree.png, candleOn.png, candleOff.png) in den
Build-Ordner kopieren. Sie finden diesen, indem Sie in Visual Studio im
Projektmappen-Explorer einen Rechtsklick auf das Projekt durchführen und
"Ordner in Datei-Explorer öffnen" auswählen.
*/
throwException("Konnte das Baumbild nicht laden!");
}
Die Funktion throwException
wird später noch implementiert. Die für das Programm benötigten drei Bilder des Baums, der erloschenen und der angezündeten Kerze finden Sie zusammen mit der fertigen Codedatei am Ende dieses Beitrags. Nun geht es um die grundlegende Struktur: Das Fenster sowie die Kerzen.
// Ein Nana-Formular als Basis des Fensters anlegen
nana::form window(nana::API::make_center(tree.size().width, tree.size().height));
window.caption("C++ Weihnachtsspecial 2021");
// Jede Codezeile beim Anlegen der Liste entspricht einem Stockwerk des Baumes
std::vector<Candle> candles = { Candle(290, 200),
Candle(200, 300), Candle(300, 330), Candle(400, 315),
Candle(120, 430), Candle(250, 450), Candle(350, 450), Candle(480, 450),
Candle(70, 580), Candle(170, 620), Candle(290, 630), Candle(420, 625), Candle(540, 600) };
Die Kerzen werden als Objekte modelliert und mit x- und y-Koordinaten angelegt. Die Klassendefinition folgt gleich, nachdem der Rest der main
-Funktion besprochen wurde. Folgen Sie solange dem im Abschnitt 4.3 des Buches erläuterten Prinzip des “Wishful Programmings” und nehmen Sie einfach an, Sie hätten schon eine solche Klasse, die Ihnen alle Wünsche erfüllt. Nun sollen die Grafiken alle gezeichnet werden:
// Zeichnen des Baumes und der Kerzen
nana::drawing drawing(window);
drawing.draw([&](nana::paint::graphics& graphics)
{
tree.paste(graphics, nana::point(0, 0));
for (Candle& candle : candles)
{
candle.draw(graphics);
}
graphics.string(nana::point(10, static_cast<int>(window.size().height) - 20),
"C++ Schnelleinstieg: Programmieren lernen in 14 Tagen. Philipp Hasper, 2021");
});
Das Zeichnen der weihnachtlichen Szenerie übernimmt die Nana-Klasse nana::drawing
innerhalb einer Lambdafunktion. Sie sehen hier zwei praktische Methoden in Aktion:
tree.paste()
kopiert das Bild des Baumes an eine bestimmte Stelle – hier an die Stelle (0,0) was der linken oberen Ecke entspricht. Hierfür muss auch noch dasgraphics
-Object übergeben werden, welches aus dem Parameter der Lambdafunktion kommt.graphics.string()
schreibt einen Text an eine bestimmte Stelle. Hier soll es in die linke untere Ecke, daher muss für die y-Koordinate von der Höhe des Fensters genug Platz abgezogen werden, sodass der Text noch hinpasst.
Zum Schluss fehlen noch die Klickereignisse. Da die Kerzen als Grafiken gezeichnet wurden, haben sie kein automatisches Klick-Event wie zum Beispiel eine Schaltfläche. Daher muss jeder Klick ins Fenster abgefangen und darauf überprüft werden, ob seine Koordinaten innerhalb einer Kerze liegen:
// Jeden Klick überprüfen, ob er eine Kerze trifft
window.events().click([&](const nana::arg_click& event) {
if (event.mouse_args == nullptr)
{
// Event kann nicht verarbeitet werden
return;
}
for (Candle& candle : candles)
{
if (candle.isClicked(event.mouse_args->pos))
{
candle.toggle();
/* Hier könnte man mit einem return die Schleife verlassen.
Da sich aber bei falscher Platzierung die Kerzen überlagern
könnten, wird die Schleife noch zu Ende geführt.
*/
}
}
// Zeichnung wiederholen, sodass das Bild aktualisiert wird.
drawing.update();
});
// Fenster anzeigen und Nana starten
window.show();
nana::exec();
return 0;
}
Ob eine Kerze geklickt wurde, wird innerhalb der Candle-Klasse überprüft. Auch die Reaktion auf einen Klick ist in dieser Klasse implementiert. Fügen Sie daher diese Klasse oberhalb der main
-Funktion ein:
// Klasse zum Zeichnen einer Kerze in zwei Zuständen (angezündet, ausgeblasen)
class Candle {
private:
nana::paint::image candleOn;
nana::paint::image candleOff;
bool isOn = false;
nana::rectangle rectangle;
public:
Candle(int centerX, int centerY)
: candleOn("candleOn.png"),
candleOff("candleOff.png")
{
if (candleOn.empty() || candleOff.empty())
{
throwException("Konnte die Kerzenbilder nicht laden!");
}
if (candleOn.size() != candleOff.size())
{
throwException("Kerzenbilder haben nicht die gleiche Größe!");
}
/* Die an den Konstruktor übergebene x - und y - Koordinate sollen das Zentrum
der Kerze angeben, daher müssen Sie für das Ziel-Rechteck umgerechnet werden.
*/
nana::size size = candleOn.size();
rectangle = nana::rectangle(centerX - size.width/2,
centerY - size.height/2,
size.width,
size.height);
}
void draw(nana::paint::graphics& graphics)
{
if (isOn)
{
candleOn.paste(graphics, rectangle.position());
}
else
{
candleOff.paste(graphics, rectangle.position());
}
}
bool isClicked(const nana::point& point)
{
// Diese Methode testet, ob der Punkt innerhalb des Rechtecks liegt
return rectangle.is_hit(point);
}
void toggle()
{
isOn = !isOn;
}
};
Diese Klasse handhabt zwei verschiedene Bilder, die an einem Punkt zentriert angezeigt werden sollen. Abhängig vom Zustand (isOn
), wird das eine oder das andere Bild gezeichnet. Zur einfacheren Platzierung wird beim Erzeugen des Objektes das gewünschte Zentrum der Kerze angegeben, was dann innerhalb des Konstruktors in ein entsprechendes Rechteck umgerechnet werden muss.
Nun fehlt noch eine Hilfsfunktion, die eine Fehlermeldung auf der Konsole ausgibt und dann einen Fehler wirft. Fügen Sie diese am Anfang der Datei, nach den include-Befehlen ein:
// Funktion, einen Fehler auf der Konsole ausgibt und dann einen Fehler wirft
void throwException(const std::string& msg)
{
std::cout << msg << std::endl;
throw std::exception(msg.c_str());
}
Und das war auch schon das ganze Programm. Frohe Feiertage und einen guten Rutsch! Den vollständigen Code und die Bilder können Sie hier herunterladen:
Jekyll plugin to bundle zip archives
The website for my recently published C++ book, cpp.hasper.info (German), was made with the static site generator Jekyll. It contains additional information, such as the code of all projects inside the book, as well as the sample solution for the exercises at the end of each chapter.
The reason why I used a static site generator in the first place was that I had all the code files organized in a folder, equipped with a CMake file which made sure the projects compile and are statically analyzed (I used cppcheck and cpplint, albeit with a very reduced set of checks due to the nature of the code examples). In order to not destroy this automation by copy-pasting code into a CMS, the site had to be generated around the code files.
I also wanted to enable the download of all C++ files as zip archive on a per-chapter basis. Again – I did not want to manually create this archive, in case I had to change some code in the future. So I wrote a Jekyll plugin which bundles given files into a zip archive which then can be placed behind a download link.
How it is used:
Filenames as multiple parameters:
{% zip archiveToCreate.zip file1.txt file2.txt %}
Spaces in filenames:
{% zip archiveToCreate.zip file1.txt folder/file2.txt 'file with spaces.txt' %}
A variable to contain a list of files is also possible:
{% zip ziparchiveToCreate.zip {{ chapter_code_files }} %}
The plugin code:
The plugin can be found here: https://github.com/PhilLab/jekyll-zip-bundler
# frozen_string_literal: true
# Copyright 2021 by Philipp Hasper
# MIT License
# https://github.com/PhilLab/jekyll-zip-bundler
require 'jekyll'
require 'zip'
# ~ gem 'rubyzip', '~>2.3.0'
module Jekyll
# Valid syntax:
# {% zip archiveToCreate.zip file1.txt file2.txt %}
# {% zip archiveToCreate.zip file1.txt folder/file2.txt 'file with spaces.txt' %}
# {% zip {{ variableName }} file1.txt 'folder/file with spaces.txt' {{ otherVariableName }} %}
# {% zip {{ variableName }} {{ VariableContainingAList }} %}
class ZipBundlerTag < Liquid::Tag
VARIABLE_SYNTAX = /[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)/mx.freeze
CACHE_FOLDER = '.jekyll-cache/zip_bundler/'
def initialize(tag_name, markup, tokens)
super
# Split by spaces but only if the text following contains an even number of '
# Based on https://stackoverflow.com/a/11566264
# Extended to also not split between the curly brackets of Liquid
# In addition, make sure the strings are stripped and not empty
@files = markup.strip.split(/\s(?=(?:[^'}]|'[^']*'|{{[^}]*}})*$)/)
.map(&:strip)
.reject(&:empty?)
end
def render(context)
# First file is the target zip archive path
target, files = resolve_parameters(context)
abort 'zip tag must be called with at least two files' if files.empty?
zipfile_path = CACHE_FOLDER + target
FileUtils.makedirs(File.dirname(zipfile_path))
# Create the archive. Delete file, if it already exists
File.delete(zipfile_path) if File.exist?(zipfile_path)
Zip::File.open(zipfile_path, Zip::File::CREATE) do |zipfile|
files.each do |file|
# Two arguments:
# - The name of the file as it will appear in the archive
# - The original file, including the path to find it
zipfile.add(File.basename(file), file)
end
end
puts "Created archive #{zipfile_path}"
# Add the archive to the site's static files
site = context.registers[:site]
site.static_files << Jekyll::StaticFile.new(site, "#{site.source}/#{CACHE_FOLDER}",
File.dirname(target),
File.basename(zipfile_path))
# No rendered output
''
end
def resolve_parameters(context)
# Resolve the given parameters to a file list
target, files = @files.map do |file|
next file unless file.match(VARIABLE_SYNTAX)
# This is a variable. Look it up.
context[file]
end
[target, files]
end
end
end
Liquid::Template.register_tag('zip', Jekyll::ZipBundlerTag)
C++ Schnelleinstieg
English summary: I have published a C++ beginners book. As it is in German, so is this post.
Auf 304 kompakten Seiten bekommen Sie einen fundierten Einstieg in die C++ Programmierung. Besonders hervorzuheben und für ein Einsteigerbuch ungewöhnlich ist die Einbindung von diversen Open-Source-Bibliotheken – professionell gemanaged mittels des Paketmanagers vcpkg. Im Rahmen des Buches erlernen Sie auch fortgeschrittene Fähigkeiten wie die Programmierung graphischer Oberflächen und das Arbeiten mit Web-APIs, wie der von Wikipedia. Das Buch legt wert auf einen verständlichen aber dennoch professionellen Programmierstil und gibt einen Ausblick über das Programmieren im professionellen Umfeld, sowie Anküpfungspunkte zum selbstständigen Erweitern der eigenen Kenntnisse.
Das Begleitmaterial sowie das Inhaltsverzeichnis und eine Leseprobe finden Sie auf https://cpp.hasper.info/
Beim Verlag kaufen: C++ Schnelleinstieg
Bei Amazon kaufen: C++ Schnelleinstieg
Natürlich freue ich mich sehr über eine Bewertung des Buches.
- Alle Grundlagen einfach erläutert
- Objektorientierte Programmierung
- Einsatz von Open-Source-Bibliotheken
- Grafische Benutzungsoberflächen (GUI)
- Internetanfragen und JSON-Parsing
- Zeiger und virtuelle Methoden
- Fehlersuche und Debugging
- Moderner Programmierstil
- Programmcode, Lösungen und Glossar zum Download
»Während viele andere C++-Lehrbücher ihr Heil in der Erklärung jedes noch so obskuren Details suchen, fokussiert sich Hasper auf alles Wichtige, das Entwicklerinnen und Entwickler bei der alltäglichen Arbeit brauchen. Haben sie bisher mit Sprachen wie JavaScript oder C# Erfahrung gesammelt, findet sich hier ein hilfreicher Wegweiser durch den Dschungel des modernen C++.«
(Heise Online, 09/2021)
»Didaktisch gut aufgebaut.«
(ekz Bibliotheksservice, 10/2021)
Choir practice during the Covid-19 Pandemic
Singing proved to be a challenge during the Corona pandemic, both because of hygienic considerations and contact restrictions.
While conferencing software like Zoom, Teams or Skype worked for other types of remote collaboration, making music together proved to be impossible due to the latency – having musicians with more than 0.5 seconds delay trying to interact with each other does not work. Some choirs resort to a many-to-one setting, meaning that everyone is muted except the choir director who plays the score on the piano. This results in everyone singing at home alone, just listening to the piano – and the director is fully on their own, with occasional questions like “did anyone have a problem here?” left as their only feedback channel.
Still, this is better than not making music at all, but luckily there is a better (but way more compilated) solution for it. We used the open source tools Jamulus and Jitsi.
Jamulus for audio
Jamulus is a software dedicated to low-latency audio connections over the internet. You can set up your own server but we used a paid service for this, as it proved to be fast and reliable: https://melomax.live/booking/ It is even possible to just start the server on your home PC, but the experience in terms of quality and latency was much worse!
I would only recommend to start a server on your home PC for the first phase and this is the “getting everyone up to speed” phase, which can take a couple of hours. Unfortunately, Jamulus is not an out-of-the-box solution and often has issues with the audio hardware.
- Have every singer read through the setup guide here: https://jamulus.io/wiki/Getting-Started . At the very minimum they need cable headphones. An Ethernet cable and an external microphone are highly recommended.
- Have a tech-savvy person available who starts a preliminary server on their PC, because it is cheaper there.
- Invite the other singers in small groups (more than four would likely overburden your preliminary server) to test their setup. Prepare for a couple of hours of tech support here (again, you need at least one tech-savvy person for this). In general, you need to experiment which audio settings work for each individual.
- When you are confident that the singers are ready, book your sessions on Melomax and invite the whole group there.
- Be prepared for additional tech support and expect that setups which worked previously might miraculously break later.
The setup can be very frustrating, but the outcome was really worth it – we had a really emotional situation when we were able to digitally sing together after months of silence!
Jitsi for video
Jamulus only supports audio and it is already sufficient for making music together. But the main drawback is that you still need a “main reference” i.e., a piano to sing along. This is needed to cope with the minimal but still existing latency – everyone has to concentrate on it so the choir does not get slower and slower.
But good news! It is possible to also enable low-latency video, using a second software named Jitsi. The trick is to setup your own Jitsi server and configure it to be low-latency. In essence, this means to
- Reduce the video quality to a bare minimum (e.g. 320 x 190 pixels)
- Disable Jitsi audio
- Disable encryption
To achieve this, you need to setup a Jitsi server (this time, an existing service won’t be enough as you need detailed control over the instance). Then, add the following settings to the /etc/jitsi/meet/<server name>-config.js
:
{
testing: {
disableE2EE: true,
p2pTestMode: false
},
enableNoisyMicDetection: false,
startWithAudioMuted: true,
startSilent: true,
stereo: true,
disableAP: true,
resolution: 200,
constraints: {
video: {
aspectRatio: 16 / 9,
height: {
ideal: 200,
max: 200,
min: 100
}
}
},
enableLayerSuspension: true,
prejoinPageEnabled: false,
p2p: {
enabled: false,
}
}
The result
This technical setup allowed us to come together during the lock down(s) and hence, we were able to record the result quickly when we were allowed to meet again. Still, with unusually large gaps between every singer, but at least physical again:
You can find more about the choir (in German) on https://vokalspezial.de/
How to remove Unity Services from your project
I recently found myself in the situation that a Unity project which changed ownership, still linked to now non-existing Unity Services. It constantly gave me warning messages like:
Unable to access Unity services. Please log in or request membership to this project to use these services
These were only warnings, so nothing particularly dangerous, but still I wanted to get rid of them. As it took me multiple iterations to solve it and there are multiple unsolved or incomplete post about it, I wanted to share here how I finally managed to solve this problem:
- Navigate to the file <
Your project folder>/ProjectSettings/ProjectSettings.asset
and open it with a text editor - In the section
cloudServicesEnabled
, set all to 0 - Set the
cloudProjectId
empty (i.e. a space after the colon)
Especially the space part took me a while to figure it out. The respective sections of the Unity project’s settings file now look similar to this:
...
vrEditorSettings:
daydream:
daydreamIconForeground: {fileID: 0}
daydreamIconBackground: {fileID: 0}
cloudServicesEnabled:
UNet: 0
luminIcon:
m_Name:
m_ModelFolderPath:
m_PortalFolderPath:
luminCert:
m_CertPath:
m_SignPackage: 1
luminIsChannelApp: 0
luminVersion:
m_VersionCode: 1
m_VersionName:
apiCompatibilityLevel: 6
cloudProjectId:
framebufferDepthMemorylessMode: 0
...
Having these modifications in the project settings made the warning disappear, as the unused cloud services are now properly removed from the project. However, given that Unity sometimes changes their framework and configurations, this might get out-dated some day in the near future. If this doesn’t work anymore when you are trying it, please drop me a message, so I can retry and update the article. But so far, the warning has not popped up again in any of my Unity projects.