1564715760
Web apps would run better if heavy computations could be performed in the background, rather than compete with the user interface.
We need all apps to be very fast and highly responsive. Like lightning fast and responsive. So in that way, we have researched and found many ways of making our apps very fast, to keep our users. It is said humans are very impatient wait for an app to load or run some computations, we will leave a site if it takes ~3s
to run/load. So check your analytics you may have a wonderful website but may have low visitors because of how performant (low or high) your app is, you need to check it today. In this post, we will discuss one of the optimization tricks to make our Angular apps highly performant.
Web Worker is really a worker, working its ass off because of the heavy load it is given. In computing context, Web Worker is a background thread that is created alongside/parallel the main thread where heavy computations are done to prevent drag on the main thread.
Web worker is one of the many workers we have in web. There are
All do the same thing, creating a background thread but run different functions. So the similar thing they do is create a parallel thread.
What is a thread? Whenever a program runs, it is allocated a space(memory address space) in RAM where its code and data are stored and executed by the CPU instruction by instruction one at a time. The memory space allocated is called the main thread. A thread is a piece of code that has its codespace inside a process. If a CPU supports multi-threading, it can run multiple threads at a time.
What is multi-threading? This is the concurrent execution/running of threads at the same time in a process. In Win, we create threads using the CreateThreadEx(…) API. This creates a codespace parallel to the main thread. The CPU executes both threads by executing one for a time saves its context, load the next thread in the registers and continues with its execution. This context switch happens on a scale of ~billionth(1/10^9) of a second that it appears to us to happen at the same time and very fast.
So in Worker, the JS engine creates a new thread and loads the JS script in the thread. In multi-threading, threads communicate with each other using shared resources. The shared resource is placed in a central place in the RAM where the threads can read/write to the resource thereby communicating with each other.
In the browser, we have the main thread which is called the DOM thread, during the loading of a new script in a tab, a DOM thread is created where the JS engine loads, parses and executes the JS files in the page. The Worker will create a new thread called the Worker Thread that will run a JS script parallel to the DOM thread. The JS script run by the Worker thread would not have a reference to the DOM because it is running in a different environment where no DOM APIs exists.
Using Web Worker
Like we said earlier, Workers don’t have access to the DOM APIs. They can’t access any of the following:
window
objectdocument
objectWeb Workers can access:
navigator
objectlocation
object (read-only)XMLHttpRequest
setTimeout()
, clearTimeout()
, setInterval()
, clearInterval()
Web Workers can also access
Cache
objectWeb workers can run asynchronously and synchronously. Web Workers run their code from top to down then they enter into event loop, executing tasks/callbacks scheduled by event (APIs like setTimeout, setInterval, etc). First, they run synchronously executing the script then they enter asynchronous listening for events and executing them.
Web Worker provides methods/APIs that we can use to run our JS script. To instantiate a Worker, we run this:
const webWorker = new Worker('./script.js')
The JS script file is passed to the Worker when instantiating it. The Worker will create a new thread, parse and generate machine code from the JS script file, the machine code will be loaded in the new webWorker thread memory address space. Then the CPU will concurrently run the DOM thread and the webWorker thread.
Web Worker can send messages to the DOM thread that spawned it. It has several event listeners that trigger a registered callback when the event is fired.
onmessage
This is triggered when a message is received.
webWorker.onmessage = function(event) {
// ...
}
onerror
This is triggered when an error is thrown in the Worker.
webWorker.onerror = function(event) {
// ...
}
We can send messages using the postMessage API
From the DOM to the Web Worker
webWorker.postMessage(data)
From the Web Worker to the DOM thread:
postMessage(data)
We can listen for events using the addEventListener API
webWorker.addEventListener('message', function(evt) {
// ...
})
It seems Web workers are used in vanilla JavaScript apps, most of us are addicted to Angular. We, Angular developers, have tried to use Web Workers in Angular but eh Webpack configuration and all proved very painful to setup. Up until the release of Angular CLI v8, using Web Workers in Angular, its setup, compiling, bundling and code-splitting was made easy by the CLI tool.
To generate a Web Worker, we run the ng g web-worker
command:
ng g web-worker webworker
This will generate webworker.ts
file in the src/app
of an Angular app. The web-worker
tells the CLI tools that the file would be used by a Worker.To demonstrate how to use Web worker in Angular to optimize its performance. Let’s say we have an app that calculates Fibonacci numbers. Finding Fibonacci numbers in the DOM thread will kinda impact the UI experience because the DOM and the user interactions would freeze until the number is found.Starting, our app would be like this:
// webWorker-demo/src/app/app.component.ts@Component({
selector: 'app',
template: `
<div>
<input type="number" [(ngModel)]="number" placeholder="Enter any number" />
<button (click)="calcFib">Calc. Fib</button>
</div>
<div>{{output}}</div>
`
})
export class App {
private number
private output
calcFib() {
this.output =fibonacci(this.number)
}
}function fibonacci(num) {
if (num == 1 || num == 2) {
return 1
}
return fibonacci(num - 1) + fibonacci(num - 2)
}
Calculating Fibonacci numbers is recursive, passing small numbers like 0–900 would have no performance impact. Imagine passing ~10,000. That’s when we will begin to notice performance drag. Like we said the best bet is to move the fibonacci function or algorithm to execute in another thread. So no matter how large the number is, it will not be felt in the DOM thread.
So we scaffold a Web Worker file:
ng g web-worker webWorker
and move the fibonacci function into the file:
// webWorker-demo/src/app/webWorker.ts
function fibonacci(num) {
if (num == 1 || num == 2) {
return 1
}
return fibonacci(num - 1) + fibonacci(num - 2)
}self.addEventListener('message', (evt) => {
const num = evt.data
postMessage(fibonacci(num))
})
Now we will edit the app.component.ts to add Web Worker
// webWorker-demo/arc/app/app.component.ts@Component({
selector: 'app',
template: `
<div>
<input type="number" [(ngModel)]="number" placeholder="Enter any number" />
<button (click)="calcFib">Calc. Fib</button>
</div>
<div>{{output}}</div>
`
})
export class App implements OnInit{
private number
private output
private webworker: Worker ngOnInit() {
if(typeof Worker !== 'undefined') {
this.webWorker = new Worker('./webWorker')
this.webWorker.onmessage = function(data) {
this.output = data
}
}
} calcFib() {
this.webWorker.postMessage(this.number)
}
}
Our code is now 😘. We added ngOnInit lifecycle hook in our component so to initialize the Web Worker with the Web Worker file we generated earlier. We registered to listen to messages sent fro the Web Worker in the onmessage
handler any data we get we will display it in the DOM.
We made the calcFib function to send the number to Web Worker. This below in webWorker would capture the number
self.addEventListener('message', (evt) => {
const num = evt.data
postMessage(fibonacci(num))
})
and processes the Fibonacci number then send the result back to the DOM thread. The onmessage we set up in the app.component
ngOnInit() {
if(typeof Worker !== 'undefined') {
this.webWorker = new Worker('./webWorker')
this.webWorker.onmessage = function(data) {
this.output = data
}
}
}
would receive the result in data
then we will display the result in the DOM using the {{output}}
interpolation.
During the processing of the Fibonacci numbers, the DOM thread would be left focusing on the user interactions while the webWorker would do the heavy processing.
Let’s have a look at a simple example that calculates prime numbers:
// primes-demo/src/app/app.component.ts@Component({
selector: 'app',
template: `
<div>
<input type="number" [(ngModel)]="number" placeholder="Enter any number" />
<button (click)="calcPrimes">Calc. Prime Numbers</button>
</div>
<div>{{output}}</div>
`
})
export class App {
private number
private output calcPrimes() {
this.output = this.getPrimes(this.number)
} getPrimes(number) {
if (typeof number == "number") {
if(number<0){
return "negative integers can not be prime";
}
if(number==0){
return "zero is not a prime number";
}
if(number==1){
return "1 is not a prime number";
}
var nonprimes = [], // Array of non prime numbers
var i,j,primes = []; // Array of prime numbers for (i = 2; i <= number; ++i) {
if (!nonprimes[i]) {
// i has not been marked -- it is prime
primes.push(i);
for (j = i << 1; j <= number; j += i) {
nonprimes[j] = true;
}
}
}
return primes; // Array of prime numbers
}
else{
return "invalid input";
}
}
}
Number entered in the input box is held in the number property, when the Calc. Prime Numbers
button is clicked the calcPrimes
method is called which calls the getPrimes function, this contains the algorithm to calculate prime numbers of a number, this function returns an array that contains the number primes, then the calcPrime
assigns it to the output property which is displayed on the browser.
Prime number calculation can become expensive when the number increases, so prime number calculation is a good candidate to be offloaded to the Web Worker thread.
So we scaffold a new Web Worker file:
ng g web-worker primesWorker
Then we add the code to primesWorker:
// primes-demo/src/app/primesWorker.tsfunction getPrimes(number) {
if (typeof number == "number") {
if(number<0){
return "negative integers can not be prime";
}
if(number==0){
return "zero is not a prime number";
}
if(number==1){
return "1 is not a prime number";
}
var nonprimes = [], // Array of non prime numbers
var i,j,primes = []; // Array of prime numbers for (i = 2; i <= number; ++i) {
if (!nonprimes[i]) {
// i has not been marked -- it is prime
primes.push(i);
for (j = i << 1; j <= number; j += i) {
nonprimes[j] = true;
}
}
}
return primes; // Array of prime numbers
}
else{
return "invalid input";
}
}self.addEventListener('message', (evt) => {
const num = evt.data
postMessage(getPrimes(num))
})
Then our app.component.ts will be rewritten to this:
// primes-demo/src/app/app.component.ts@Component({
selector: 'app',
template: `
<div>
<input type="number" [(ngModel)]="number" placeholder="Enter any number" />
<button (click)="calcPrimes">Calc. Prime Numbers</button>
</div>
<div>{{output}}</div>
`
})
export class App implements OnInit {
private number
private output
private primesWorker: Worker ngOnInit() {
if(typeof Worker !== 'undefined') {
this.primesWorker = new Worker('./primesWorker')
this.primesWorker.onmessage = function(data) {
this.output = data
}
}
} calcPrimes() {
this.primesWorker.postMessage(this.number)
}
}
The prime number calculation is now being done in another thread leaving the DOM thread free.The thing here is that the time it would take a heavy calculation to complete in the main thread is the same as in the Worker thread. Moving it off to the Worker thread doesn’t reduce the calculation speed, it just prevents the DOM thread from locking and becoming unresponsive.Terminating a workerAccording to Using Web Worker APIs — Wikipedia
If you need to immediately terminate a running worker from the main thread, you can do so by calling the worker’s terminate method:
webWorker.terminate();
The worker thread is killed immediately.In our Angular example, we didn’t kill the Worker thread. When the app.component.ts
is destroyed the Worker thread will still be left open and be hanging around. This is very bad practice, we should clean up the Worker thread when we are done with it.To do so, we will utilize the ngOnDestroy
hook in Angular. This hook is what Angular calls when a component is being destroyed, so we will terminate the Worker thread there.
// primes-demo/src/app/app.component.ts@Component({
selector: 'app',
template: `
<div>
<input type="number" [(ngModel)]="number" placeholder="Enter any number" />
<button (click)="calcPrimes">Calc. Prime Numbers</button>
</div>
<div>{{output}}</div>
`
})
export class App implements OnInit, OnDestroy {
private number
private output
private primesWorker: Worker ngOnInit() {
if(typeof Worker !== 'undefined') {
this.primesWorker = new Worker('./primesWorker')
this.primesWorker.onmessage = function(data) {
this.output = data
}
}
} calcPrimes() {
this.primesWorker.postMessage(this.number)
} ngOnDestroy() {
his.primesWorker.terminate()
}
}
In the fibonacci example:
// webWorker-demo/arc/app/app.component.ts@Component({
selector: 'app',
template: `
<div>
<input type="number" [(ngModel)]="number" placeholder="Enter any number" />
<button (click)="calcFib">Calc. Fib</button>
</div>
<div>{{output}}</div>
`
})
export class App implements OnInit, OnDestroy {
private number
private output
private webworker: Worker ngOnInit() {
if(typeof Worker !== 'undefined') {
this.webWorker = new Worker('./webWorker')
this.webWorker.onmessage = function(data) {
this.output = data
}
}
} calcFib() {
this.webWorker.postMessage(this.number)
} ngOnDestroy() {
this.webWorker.terminate()
}
}
In this post, we saw what Web Worker is, its APIS and how to use it. Further down, we saw how to use the Angular CLI to easily add Web Worker to our Angular apps and we saw its usage in Angular on how we moved our Fibonacci calculation to the Worker thread.
In as much Worker is good in offloading our work to the Worker thread, we should also try to write optimizable code in the Worker script because it would take the same time in the main thread. So adding Worker to a good JS code would be lit, your app would be blazingly fast 🚀.
If you have any question regarding this or anything I should add, correct or remove, feel free to comment, email or DM me.
#web-development #web-service #angular
1621426329
AppClues Infotech is one of the leading Enterprise Angular Web Apps Development Company in USA. Our dedicated & highly experienced Angular app developers build top-grade Angular apps for your business with immersive technology & superior functionalities.
For more info:
Website: https://www.appcluesinfotech.com/
Email: info@appcluesinfotech.com
Call: +1-978-309-9910
#top enterprise angular web apps development company in usa #enterprise angular web apps development #hire enterprise angular web apps developers #best enterprise angular web app services #custom enterprise angular web apps solution #professional enterprise angular web apps developers
1667425440
Perl script converts PDF files to Gerber format
Pdf2Gerb generates Gerber 274X photoplotting and Excellon drill files from PDFs of a PCB. Up to three PDFs are used: the top copper layer, the bottom copper layer (for 2-sided PCBs), and an optional silk screen layer. The PDFs can be created directly from any PDF drawing software, or a PDF print driver can be used to capture the Print output if the drawing software does not directly support output to PDF.
The general workflow is as follows:
Please note that Pdf2Gerb does NOT perform DRC (Design Rule Checks), as these will vary according to individual PCB manufacturer conventions and capabilities. Also note that Pdf2Gerb is not perfect, so the output files must always be checked before submitting them. As of version 1.6, Pdf2Gerb supports most PCB elements, such as round and square pads, round holes, traces, SMD pads, ground planes, no-fill areas, and panelization. However, because it interprets the graphical output of a Print function, there are limitations in what it can recognize (or there may be bugs).
See docs/Pdf2Gerb.pdf for install/setup, config, usage, and other info.
#Pdf2Gerb config settings:
#Put this file in same folder/directory as pdf2gerb.pl itself (global settings),
#or copy to another folder/directory with PDFs if you want PCB-specific settings.
#There is only one user of this file, so we don't need a custom package or namespace.
#NOTE: all constants defined in here will be added to main namespace.
#package pdf2gerb_cfg;
use strict; #trap undef vars (easier debug)
use warnings; #other useful info (easier debug)
##############################################################################################
#configurable settings:
#change values here instead of in main pfg2gerb.pl file
use constant WANT_COLORS => ($^O !~ m/Win/); #ANSI colors no worky on Windows? this must be set < first DebugPrint() call
#just a little warning; set realistic expectations:
#DebugPrint("${\(CYAN)}Pdf2Gerb.pl ${\(VERSION)}, $^O O/S\n${\(YELLOW)}${\(BOLD)}${\(ITALIC)}This is EXPERIMENTAL software. \nGerber files MAY CONTAIN ERRORS. Please CHECK them before fabrication!${\(RESET)}", 0); #if WANT_DEBUG
use constant METRIC => FALSE; #set to TRUE for metric units (only affect final numbers in output files, not internal arithmetic)
use constant APERTURE_LIMIT => 0; #34; #max #apertures to use; generate warnings if too many apertures are used (0 to not check)
use constant DRILL_FMT => '2.4'; #'2.3'; #'2.4' is the default for PCB fab; change to '2.3' for CNC
use constant WANT_DEBUG => 0; #10; #level of debug wanted; higher == more, lower == less, 0 == none
use constant GERBER_DEBUG => 0; #level of debug to include in Gerber file; DON'T USE FOR FABRICATION
use constant WANT_STREAMS => FALSE; #TRUE; #save decompressed streams to files (for debug)
use constant WANT_ALLINPUT => FALSE; #TRUE; #save entire input stream (for debug ONLY)
#DebugPrint(sprintf("${\(CYAN)}DEBUG: stdout %d, gerber %d, want streams? %d, all input? %d, O/S: $^O, Perl: $]${\(RESET)}\n", WANT_DEBUG, GERBER_DEBUG, WANT_STREAMS, WANT_ALLINPUT), 1);
#DebugPrint(sprintf("max int = %d, min int = %d\n", MAXINT, MININT), 1);
#define standard trace and pad sizes to reduce scaling or PDF rendering errors:
#This avoids weird aperture settings and replaces them with more standardized values.
#(I'm not sure how photoplotters handle strange sizes).
#Fewer choices here gives more accurate mapping in the final Gerber files.
#units are in inches
use constant TOOL_SIZES => #add more as desired
(
#round or square pads (> 0) and drills (< 0):
.010, -.001, #tiny pads for SMD; dummy drill size (too small for practical use, but needed so StandardTool will use this entry)
.031, -.014, #used for vias
.041, -.020, #smallest non-filled plated hole
.051, -.025,
.056, -.029, #useful for IC pins
.070, -.033,
.075, -.040, #heavier leads
# .090, -.043, #NOTE: 600 dpi is not high enough resolution to reliably distinguish between .043" and .046", so choose 1 of the 2 here
.100, -.046,
.115, -.052,
.130, -.061,
.140, -.067,
.150, -.079,
.175, -.088,
.190, -.093,
.200, -.100,
.220, -.110,
.160, -.125, #useful for mounting holes
#some additional pad sizes without holes (repeat a previous hole size if you just want the pad size):
.090, -.040, #want a .090 pad option, but use dummy hole size
.065, -.040, #.065 x .065 rect pad
.035, -.040, #.035 x .065 rect pad
#traces:
.001, #too thin for real traces; use only for board outlines
.006, #minimum real trace width; mainly used for text
.008, #mainly used for mid-sized text, not traces
.010, #minimum recommended trace width for low-current signals
.012,
.015, #moderate low-voltage current
.020, #heavier trace for power, ground (even if a lighter one is adequate)
.025,
.030, #heavy-current traces; be careful with these ones!
.040,
.050,
.060,
.080,
.100,
.120,
);
#Areas larger than the values below will be filled with parallel lines:
#This cuts down on the number of aperture sizes used.
#Set to 0 to always use an aperture or drill, regardless of size.
use constant { MAX_APERTURE => max((TOOL_SIZES)) + .004, MAX_DRILL => -min((TOOL_SIZES)) + .004 }; #max aperture and drill sizes (plus a little tolerance)
#DebugPrint(sprintf("using %d standard tool sizes: %s, max aper %.3f, max drill %.3f\n", scalar((TOOL_SIZES)), join(", ", (TOOL_SIZES)), MAX_APERTURE, MAX_DRILL), 1);
#NOTE: Compare the PDF to the original CAD file to check the accuracy of the PDF rendering and parsing!
#for example, the CAD software I used generated the following circles for holes:
#CAD hole size: parsed PDF diameter: error:
# .014 .016 +.002
# .020 .02267 +.00267
# .025 .026 +.001
# .029 .03167 +.00267
# .033 .036 +.003
# .040 .04267 +.00267
#This was usually ~ .002" - .003" too big compared to the hole as displayed in the CAD software.
#To compensate for PDF rendering errors (either during CAD Print function or PDF parsing logic), adjust the values below as needed.
#units are pixels; for example, a value of 2.4 at 600 dpi = .0004 inch, 2 at 600 dpi = .0033"
use constant
{
HOLE_ADJUST => -0.004 * 600, #-2.6, #holes seemed to be slightly oversized (by .002" - .004"), so shrink them a little
RNDPAD_ADJUST => -0.003 * 600, #-2, #-2.4, #round pads seemed to be slightly oversized, so shrink them a little
SQRPAD_ADJUST => +0.001 * 600, #+.5, #square pads are sometimes too small by .00067, so bump them up a little
RECTPAD_ADJUST => 0, #(pixels) rectangular pads seem to be okay? (not tested much)
TRACE_ADJUST => 0, #(pixels) traces seemed to be okay?
REDUCE_TOLERANCE => .001, #(inches) allow this much variation when reducing circles and rects
};
#Also, my CAD's Print function or the PDF print driver I used was a little off for circles, so define some additional adjustment values here:
#Values are added to X/Y coordinates; units are pixels; for example, a value of 1 at 600 dpi would be ~= .002 inch
use constant
{
CIRCLE_ADJUST_MINX => 0,
CIRCLE_ADJUST_MINY => -0.001 * 600, #-1, #circles were a little too high, so nudge them a little lower
CIRCLE_ADJUST_MAXX => +0.001 * 600, #+1, #circles were a little too far to the left, so nudge them a little to the right
CIRCLE_ADJUST_MAXY => 0,
SUBST_CIRCLE_CLIPRECT => FALSE, #generate circle and substitute for clip rects (to compensate for the way some CAD software draws circles)
WANT_CLIPRECT => TRUE, #FALSE, #AI doesn't need clip rect at all? should be on normally?
RECT_COMPLETION => FALSE, #TRUE, #fill in 4th side of rect when 3 sides found
};
#allow .012 clearance around pads for solder mask:
#This value effectively adjusts pad sizes in the TOOL_SIZES list above (only for solder mask layers).
use constant SOLDER_MARGIN => +.012; #units are inches
#line join/cap styles:
use constant
{
CAP_NONE => 0, #butt (none); line is exact length
CAP_ROUND => 1, #round cap/join; line overhangs by a semi-circle at either end
CAP_SQUARE => 2, #square cap/join; line overhangs by a half square on either end
CAP_OVERRIDE => FALSE, #cap style overrides drawing logic
};
#number of elements in each shape type:
use constant
{
RECT_SHAPELEN => 6, #x0, y0, x1, y1, count, "rect" (start, end corners)
LINE_SHAPELEN => 6, #x0, y0, x1, y1, count, "line" (line seg)
CURVE_SHAPELEN => 10, #xstart, ystart, x0, y0, x1, y1, xend, yend, count, "curve" (bezier 2 points)
CIRCLE_SHAPELEN => 5, #x, y, 5, count, "circle" (center + radius)
};
#const my %SHAPELEN =
#Readonly my %SHAPELEN =>
our %SHAPELEN =
(
rect => RECT_SHAPELEN,
line => LINE_SHAPELEN,
curve => CURVE_SHAPELEN,
circle => CIRCLE_SHAPELEN,
);
#panelization:
#This will repeat the entire body the number of times indicated along the X or Y axes (files grow accordingly).
#Display elements that overhang PCB boundary can be squashed or left as-is (typically text or other silk screen markings).
#Set "overhangs" TRUE to allow overhangs, FALSE to truncate them.
#xpad and ypad allow margins to be added around outer edge of panelized PCB.
use constant PANELIZE => {'x' => 1, 'y' => 1, 'xpad' => 0, 'ypad' => 0, 'overhangs' => TRUE}; #number of times to repeat in X and Y directions
# Set this to 1 if you need TurboCAD support.
#$turboCAD = FALSE; #is this still needed as an option?
#CIRCAD pad generation uses an appropriate aperture, then moves it (stroke) "a little" - we use this to find pads and distinguish them from PCB holes.
use constant PAD_STROKE => 0.3; #0.0005 * 600; #units are pixels
#convert very short traces to pads or holes:
use constant TRACE_MINLEN => .001; #units are inches
#use constant ALWAYS_XY => TRUE; #FALSE; #force XY even if X or Y doesn't change; NOTE: needs to be TRUE for all pads to show in FlatCAM and ViewPlot
use constant REMOVE_POLARITY => FALSE; #TRUE; #set to remove subtractive (negative) polarity; NOTE: must be FALSE for ground planes
#PDF uses "points", each point = 1/72 inch
#combined with a PDF scale factor of .12, this gives 600 dpi resolution (1/72 * .12 = 600 dpi)
use constant INCHES_PER_POINT => 1/72; #0.0138888889; #multiply point-size by this to get inches
# The precision used when computing a bezier curve. Higher numbers are more precise but slower (and generate larger files).
#$bezierPrecision = 100;
use constant BEZIER_PRECISION => 36; #100; #use const; reduced for faster rendering (mainly used for silk screen and thermal pads)
# Ground planes and silk screen or larger copper rectangles or circles are filled line-by-line using this resolution.
use constant FILL_WIDTH => .01; #fill at most 0.01 inch at a time
# The max number of characters to read into memory
use constant MAX_BYTES => 10 * M; #bumped up to 10 MB, use const
use constant DUP_DRILL1 => TRUE; #FALSE; #kludge: ViewPlot doesn't load drill files that are too small so duplicate first tool
my $runtime = time(); #Time::HiRes::gettimeofday(); #measure my execution time
print STDERR "Loaded config settings from '${\(__FILE__)}'.\n";
1; #last value must be truthful to indicate successful load
#############################################################################################
#junk/experiment:
#use Package::Constants;
#use Exporter qw(import); #https://perldoc.perl.org/Exporter.html
#my $caller = "pdf2gerb::";
#sub cfg
#{
# my $proto = shift;
# my $class = ref($proto) || $proto;
# my $settings =
# {
# $WANT_DEBUG => 990, #10; #level of debug wanted; higher == more, lower == less, 0 == none
# };
# bless($settings, $class);
# return $settings;
#}
#use constant HELLO => "hi there2"; #"main::HELLO" => "hi there";
#use constant GOODBYE => 14; #"main::GOODBYE" => 12;
#print STDERR "read cfg file\n";
#our @EXPORT_OK = Package::Constants->list(__PACKAGE__); #https://www.perlmonks.org/?node_id=1072691; NOTE: "_OK" skips short/common names
#print STDERR scalar(@EXPORT_OK) . " consts exported:\n";
#foreach(@EXPORT_OK) { print STDERR "$_\n"; }
#my $val = main::thing("xyz");
#print STDERR "caller gave me $val\n";
#foreach my $arg (@ARGV) { print STDERR "arg $arg\n"; }
Author: swannman
Source Code: https://github.com/swannman/pdf2gerb
License: GPL-3.0 license
1619170894
Looking for the best custom AngularJS app development company? AppClues Infotech is a top-rated AngularJS app development company in USA producing robust, highly interactive and data-driven AngularJS web and mobile applications with advanced features & technologies.
For more info:
Website: https://www.appcluesinfotech.com/
Email: info@appcluesinfotech.com
Call: +1-978-309-9910
#custom angular js web app development company in usa #best angular js app development company in usa #hire angular js app developers in usa #top angular js app development company #professional angular js app developers #leading angular js app development agency
1595059664
With more of us using smartphones, the popularity of mobile applications has exploded. In the digital era, the number of people looking for products and services online is growing rapidly. Smartphone owners look for mobile applications that give them quick access to companies’ products and services. As a result, mobile apps provide customers with a lot of benefits in just one device.
Likewise, companies use mobile apps to increase customer loyalty and improve their services. Mobile Developers are in high demand as companies use apps not only to create brand awareness but also to gather information. For that reason, mobile apps are used as tools to collect valuable data from customers to help companies improve their offer.
There are many types of mobile applications, each with its own advantages. For example, native apps perform better, while web apps don’t need to be customized for the platform or operating system (OS). Likewise, hybrid apps provide users with comfortable user experience. However, you may be wondering how long it takes to develop an app.
To give you an idea of how long the app development process takes, here’s a short guide.
_Average time spent: two to five weeks _
This is the initial stage and a crucial step in setting the project in the right direction. In this stage, you brainstorm ideas and select the best one. Apart from that, you’ll need to do some research to see if your idea is viable. Remember that coming up with an idea is easy; the hard part is to make it a reality.
All your ideas may seem viable, but you still have to run some tests to keep it as real as possible. For that reason, when Web Developers are building a web app, they analyze the available ideas to see which one is the best match for the targeted audience.
Targeting the right audience is crucial when you are developing an app. It saves time when shaping the app in the right direction as you have a clear set of objectives. Likewise, analyzing how the app affects the market is essential. During the research process, App Developers must gather information about potential competitors and threats. This helps the app owners develop strategies to tackle difficulties that come up after the launch.
The research process can take several weeks, but it determines how successful your app can be. For that reason, you must take your time to know all the weaknesses and strengths of the competitors, possible app strategies, and targeted audience.
The outcomes of this stage are app prototypes and the minimum feasible product.
#android app #frontend #ios app #minimum viable product (mvp) #mobile app development #web development #android app development #app development #app development for ios and android #app development process #ios and android app development #ios app development #stages in app development
1595494844
Are you leading an organization that has a large campus, e.g., a large university? You are probably thinking of introducing an electric scooter/bicycle fleet on the campus, and why wouldn’t you?
Introducing micro-mobility in your campus with the help of such a fleet would help the people on the campus significantly. People would save money since they don’t need to use a car for a short distance. Your campus will see a drastic reduction in congestion, moreover, its carbon footprint will reduce.
Micro-mobility is relatively new though and you would need help. You would need to select an appropriate fleet of vehicles. The people on your campus would need to find electric scooters or electric bikes for commuting, and you need to provide a solution for this.
To be more specific, you need a short-term electric bike rental app. With such an app, you will be able to easily offer micro-mobility to the people on the campus. We at Devathon have built Autorent exactly for this.
What does Autorent do and how can it help you? How does it enable you to introduce micro-mobility on your campus? We explain these in this article, however, we will touch upon a few basics first.
You are probably thinking about micro-mobility relatively recently, aren’t you? A few relevant insights about it could help you to better appreciate its importance.
Micro-mobility is a new trend in transportation, and it uses vehicles that are considerably smaller than cars. Electric scooters (e-scooters) and electric bikes (e-bikes) are the most popular forms of micro-mobility, however, there are also e-unicycles and e-skateboards.
You might have already seen e-scooters, which are kick scooters that come with a motor. Thanks to its motor, an e-scooter can achieve a speed of up to 20 km/h. On the other hand, e-bikes are popular in China and Japan, and they come with a motor, and you can reach a speed of 40 km/h.
You obviously can’t use these vehicles for very long commutes, however, what if you need to travel a short distance? Even if you have a reasonable public transport facility in the city, it might not cover the route you need to take. Take the example of a large university campus. Such a campus is often at a considerable distance from the central business district of the city where it’s located. While public transport facilities may serve the central business district, they wouldn’t serve this large campus. Currently, many people drive their cars even for short distances.
As you know, that brings its own set of challenges. Vehicular traffic adds significantly to pollution, moreover, finding a parking spot can be hard in crowded urban districts.
Well, you can reduce your carbon footprint if you use an electric car. However, electric cars are still new, and many countries are still building the necessary infrastructure for them. Your large campus might not have the necessary infrastructure for them either. Presently, electric cars don’t represent a viable option in most geographies.
As a result, you need to buy and maintain a car even if your commute is short. In addition to dealing with parking problems, you need to spend significantly on your car.
All of these factors have combined to make people sit up and think seriously about cars. Many people are now seriously considering whether a car is really the best option even if they have to commute only a short distance.
This is where micro-mobility enters the picture. When you commute a short distance regularly, e-scooters or e-bikes are viable options. You limit your carbon footprints and you cut costs!
Businesses have seen this shift in thinking, and e-scooter companies like Lime and Bird have entered this field in a big way. They let you rent e-scooters by the minute. On the other hand, start-ups like Jump and Lyft have entered the e-bike market.
Think of your campus now! The people there might need to travel short distances within the campus, and e-scooters can really help them.
What advantages can you get from micro-mobility? Let’s take a deeper look into this question.
Micro-mobility can offer several advantages to the people on your campus, e.g.:
#android app #autorent #ios app #mobile app development #app like bird #app like bounce #app like lime #autorent #bird scooter business model #bird scooter rental #bird scooter rental cost #bird scooter rental price #clone app like bird #clone app like bounce #clone app like lime #electric rental scooters #electric scooter company #electric scooter rental business #how do you start a moped #how to start a moped #how to start a scooter rental business #how to start an electric company #how to start electric scooterrental business #lime scooter business model #scooter franchise #scooter rental business #scooter rental business for sale #scooter rental business insurance #scooters franchise cost #white label app like bird #white label app like bounce #white label app like lime