Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
n0ctu committed Jan 20, 2024
1 parent e0d6871 commit f446c3b
Show file tree
Hide file tree
Showing 4 changed files with 520 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
old/
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,54 @@
# luxlogo.js
A javascript program to create variations of the Luxeria logo using SVG

A javascript tool to generate (per-) versions of the [Luxeria](https://luxeria.ch/) logo in SVG format. The main part (luxlogo.js) generates SVG XML code, which can be used in HTML files. The XML output can be serialized to a file and create actual printable/cuttable SVG files.

The tool is written in pure javascript and does not require any external libraries other than libraries available in modern browsers. The idea originated from the challenge to implement the logo in [OpenSCAD](https://github.com/n0ctu/OpenSCAD-Models/tree/main/Luxeria%20Logo).

## Demo

A demo of the tool can be found [here](https://luxeria.ch/luxlogo/).

## Usage

Include the luxlogo.js file in your HTML page:

```html
<script src="luxlogo.js"></script>
```

Then instantiate the LuxLogo class and manipulate the variables. Finally call the generate() method which returns the SVG XML code.

```javascript
const logo = new LuxLogo();
logo.rotation = 20;
logo.color1 = "#ff0000";
logo.numArrows = 5;

document.getElementById("logo").innerHTML = logo.generate();
```

## Variables / Parameters

All relative variables are relative to the size variable.

| Variable | Description | Default |
| ------------------------ | ------------------------------------------ | ----------- |
| size | Size of the logo in pixels | 600 |
| color1 | Primary color of the logo | "#000000" |
| rotation | Rotation angle in degrees | 0 |
| numArrows | Number of arrows in the logo | 3 |
| relBorderThickness | Relative border thickness | 0 |
| relSpacing | Relative spacing between arrows | 8 |
| relInnerCircleDiameter | Relative diameter of the inner circle | 25 |
| relOuterCircleDiameter | Relative diameter of the outer circle | 85 |
| relOuterCircleThickness | Relative thickness of the outer circle | 12 |
| relArrowTipWidth | Relative width of the arrow tip | 25 |
| relArrowTipStart | Relative start of the arrow tip from center| 20 |
| relArrowTipEnd | Relative end of the arrow tip from center | 50 |
| relArrowNotchOffset | Relative offset of the arrow notch | 3 |
| relArrowBaseWidth | Relative width of the arrow base | 13 |

## Todo

- [ ] Fix masking/grouping to allow for proper borders
- [ ] Add support for different colors for each part (gradients?)
174 changes: 174 additions & 0 deletions js/luxlogo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
class LuxLogo {
constructor() {
// Constructor with intial values
this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
this.size = 600;
this.color1 = "#000000";
this.rotation = 0;
this.center = this.size / 2;
this.numArrows = 3;
this.relBorderThickness = 0;
this.relSpacing = 8;
this.relInnerCircleDiameter = 25;
this.relOuterCircleDiameter = 85;
this.relOuterCircleThickness = 12;
this.relArrowTipWidth = 25;
this.relArrowTipStart = 20;
this.relArrowTipEnd = 50;
this.relArrowNotchOffset = 3;
this.relArrowBaseWidth = 13;
// Calculate absolute values from relative values
this.relCalc();
}

relCalc() {
this.spacing = this.size * this.relSpacing / 100;
this.borderThickness = this.size * this.relBorderThickness / 100;
this.innerCircleDiameter = this.size * this.relInnerCircleDiameter / 100;
this.outerCircleDiameter = this.size * this.relOuterCircleDiameter / 100;
this.outerCircleThickness = this.size * this.relOuterCircleThickness / 100;
this.arrowTipWidth = this.size * this.relArrowTipWidth / 100;
this.arrowTipStart = this.center - this.size * this.relArrowTipStart / 100;
this.arrowTipEnd = this.center - this.size * this.relArrowTipEnd / 100;
this.arrowNotchOffset = this.size * this.relArrowNotchOffset / 100;

// Todo: Implement a better (easier readable) way to calculate the arrow base
this.arrowBaseWidth = this.size * this.relArrowBaseWidth / 100;
this.arrowBaseX = this.center - this.arrowBaseWidth / 2;
const circleSegmentRadius = this.innerCircleDiameter / 2 + this.spacing;
const circleSegmentLenght = this.arrowBaseWidth;
const theta = 2 * Math.asin(circleSegmentLenght / (2 * circleSegmentRadius));
this.arrowBaseOffsetCircleSegment = circleSegmentRadius - Math.sqrt(circleSegmentRadius ** 2 - (circleSegmentLenght / 2) ** 2);
this.arrowBaseHeight = this.center - this.innerCircleDiameter / 2 - this.arrowTipStart + Math.abs(this.arrowNotchOffset) + this.arrowBaseOffsetCircleSegment + 1; // Add +1 height to make sure the parts overlap
this.arrowBaseY = this.center - this.arrowBaseHeight - this.innerCircleDiameter / 2 + this.arrowBaseOffsetCircleSegment;
}

// Generic SVG functions
createCircle(cx, cy, d) {
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
const r = d / 2;
circle.setAttribute("cx", cx);
circle.setAttribute("cy", cy);
circle.setAttribute("r", r);
return circle;
}

createRectangle(x, y, w, h) {
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", x);
rect.setAttribute("y", y);
rect.setAttribute("width", w);
rect.setAttribute("height", h);
return rect;
}

createClipPath(id, clipShape) {
const clipPath = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
clipPath.setAttribute("id", id);
clipPath.appendChild(clipShape);
return clipPath;
}

createMask(id, ...maskShapes) {
const mask = document.createElementNS("http://www.w3.org/2000/svg", "mask");
mask.setAttribute("id", id);
maskShapes.forEach(shape => {
mask.appendChild(shape);
});
return mask;
}

createPolygon(points) {
const polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
polygon.setAttribute("points", points.map(p => p.join(",")).join(" "));
return polygon;
}

// Logo specific functions
createArrow() {
const polygonPoints = [
[this.center, this.arrowTipEnd],
[this.center + this.arrowTipWidth/2, this.arrowTipStart],
[this.center, this.arrowTipStart - this.arrowNotchOffset],
[this.center - this.arrowTipWidth/2, this.arrowTipStart]
];
const arrowPolygon = this.createPolygon(polygonPoints);
const arrowBase = this.createRectangle(this.arrowBaseX, this.arrowBaseY, this.arrowBaseWidth, this.arrowBaseHeight);

const arrowGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
arrowGroup.appendChild(arrowPolygon);
arrowGroup.appendChild(arrowBase);

// Add a mask to cut the arrow base
arrowGroup.setAttribute("mask", "url(#arrowMask)");

return arrowGroup;
}

arrangeArrows() {
const arrows = document.createElementNS("http://www.w3.org/2000/svg", "g");
for (let i = 0; i < this.numArrows; i++) {
const angle = (360 / this.numArrows) * i + this.rotation;
const arrow = this.createArrow();
arrow.setAttribute("transform", `rotate(${angle}, ${this.center}, ${this.center})`);
arrows.appendChild(arrow);
}
return arrows;
}

createArrowMask(id) {
const canvas = this.createRectangle(0, 0, this.size, this.size);
canvas.setAttribute("fill", "white");

const circle = this.createCircle(this.center, this.center, this.innerCircleDiameter + this.spacing);
circle.setAttribute("fill", "black");

return this.createMask(id, canvas, circle);
}

createOuterRingMask(id) {
const canvas = this.createRectangle(0, 0, this.size, this.size);
canvas.setAttribute("fill", "white");

const arrows = this.arrangeArrows();
arrows.setAttribute("fill", "black");
arrows.setAttribute("stroke", "black");
arrows.setAttribute("stroke-width", this.spacing);

const circle = this.createCircle(this.center, this.center, this.outerCircleDiameter - this.outerCircleThickness*2);
circle.setAttribute("fill", "black");

return this.createMask(id, canvas, arrows, circle);
}

generate() {

this.svg.setAttribute("width", this.size);
this.svg.setAttribute("height", this.size);

// Clear existing SVG contents
this.svg.innerHTML = "";

// Add the inner circle
const innerCircle = this.createCircle(this.center, this.center, this.innerCircleDiameter);
this.svg.appendChild(innerCircle);

// Add arrows
this.svg.appendChild(this.createArrowMask("arrowMask"));
const arrows = this.arrangeArrows();
this.svg.appendChild(arrows);

// Add outer ring
this.svg.appendChild(this.createOuterRingMask("outerRingMask"));
const outerRing = this.createCircle(this.center, this.center, this.outerCircleDiameter);
outerRing.setAttribute("mask", "url(#outerRingMask)");
this.svg.appendChild(outerRing);

// Colorize it
this.svg.setAttribute("fill", this.color1);

// Return the SVG as a string
return this.svg.outerHTML;

}
}
Loading

0 comments on commit f446c3b

Please sign in to comment.