mirror of
https://github.com/bckelley/avogadrio.git
synced 2026-08-24 10:14:10 -05:00
Merge pull request #35 from lambdacasserole/molecule-rotation
Molecule rotation
This commit is contained in:
@@ -4,7 +4,7 @@ Worship your favorite molecule by setting it as your wallpaper.
|
||||
Avogadrio is a web app that will render your favourite molecule as a desktop wallpaper from either a compund name or
|
||||
[SMILES structure](https://en.wikipedia.org/wiki/Simplified_molecular-input_line-entry_system). Molecule rendering
|
||||
is designed to be powered by [Sourire](https://github.com/tmoerman/sourire) which itself wraps the
|
||||
[Indigo cheminformatics toolkit](https://github.com/ggasoftware/indigo). Built on the
|
||||
[Indigo cheminformatics toolkit](https://github.com/ggasoftware/indigo). Built on the
|
||||
[Silex](https://github.com/silexphp/Silex) micro-framework.
|
||||
|
||||

|
||||
@@ -62,3 +62,4 @@ A big thank you to:
|
||||
* [Nile Red](https://www.youtube.com/user/TheRedNile) inspired me to build this. I wasn't that big on chemistry until I came across his videos.
|
||||
* [Jay Holtslander](https://codepen.io/j_holtslander/) put together the hamburger menu/sidebar combo in his [Pen](https://codepen.io/j_holtslander/pen/XmpMEp) on CodePen. I believe it's derived from earlier work by [maridlcrmn](https://bootsnipp.com/maridlcrmn).
|
||||
* [Thomas Moerman](https://github.com/tmoerman) created [Sourire](https://github.com/tmoerman/sourire), which really simplified the molecule rendering process.
|
||||
* [Andre Plötze](https://github.com/andrepxx) created [pure-knob](https://github.com/andrepxx/pure-knob), which is used here for the rotation knob/wheel/dial thing.
|
||||
|
||||
@@ -16,9 +16,11 @@ class MoleculeRenderer
|
||||
private $sourireUrl;
|
||||
|
||||
private $renderChiralLabels;
|
||||
|
||||
|
||||
private $customLabel;
|
||||
|
||||
|
||||
private $rotation;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a molecule rendering service.
|
||||
*
|
||||
@@ -50,8 +52,8 @@ class MoleculeRenderer
|
||||
public function renderMolecule($smiles, $color)
|
||||
{
|
||||
// Proxy into Sourire for molecule render.
|
||||
$img = Image::make($this->sourireUrl
|
||||
. 'molecule/' . rawurlencode($smiles)
|
||||
$img = Image::make($this->sourireUrl
|
||||
. 'molecule/' . rawurlencode($smiles)
|
||||
. '?render-stereo-style=' . ($this->renderChiralLabels ? 'old' : 'none') // Label chiral atoms?
|
||||
. '&render-comment-offset=16' // Give some space between label and molecule.
|
||||
. ($this->customLabel === '' ? '' : ('&render-comment=' . rawurlencode($this->customLabel))));
|
||||
@@ -60,6 +62,7 @@ class MoleculeRenderer
|
||||
list($r, $g, $b) = sscanf($color, "%02x%02x%02x");
|
||||
$unit = 100 / 255;
|
||||
$img->colorize($unit * $r, $unit * $g, $unit * $b);
|
||||
$img->rotate($this->rotation);
|
||||
|
||||
return $img; // Return molecule image.
|
||||
}
|
||||
@@ -72,6 +75,7 @@ class MoleculeRenderer
|
||||
* @param int $canvasWidth the width of the canvas to scale the molecule to
|
||||
* @param int $canvasHeight the height of the canvas to scale the molecule to
|
||||
* @param float $proportion the maximum proportion of the canvas the molecule should occupy (in either direction)
|
||||
* @param float $rotation the angle to rotate the image by
|
||||
* @return \Intervention\Image\Image
|
||||
*/
|
||||
public function renderScaledMolecule($smiles, $color, $canvasWidth, $canvasHeight, $proportion = 0.8)
|
||||
@@ -165,4 +169,26 @@ class MoleculeRenderer
|
||||
$this->customLabel = $customLabel;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the angle of rotation to apply to the molecule.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getRotation()
|
||||
{
|
||||
return $this->rotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the angle of rotation to apply to the molecule.
|
||||
*
|
||||
* @param float $rotation the angle of rotation to apply
|
||||
* @return MoleculeRenderer
|
||||
*/
|
||||
public function setRotation($rotation)
|
||||
{
|
||||
$this->rotation = $rotation;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
+56
-20
@@ -7,6 +7,9 @@ $(document).ready ->
|
||||
foregroundColor = 'ce3838'
|
||||
backgroundColor = '5f0000'
|
||||
|
||||
# Transforms.
|
||||
rotation = 0
|
||||
|
||||
# Custom label for molecule.
|
||||
customLabel = ''
|
||||
|
||||
@@ -22,7 +25,7 @@ $(document).ready ->
|
||||
|
||||
# Element to use for wallpaper preview.
|
||||
previewElement = $ 'body, html'
|
||||
|
||||
|
||||
# Download button element.
|
||||
downloadButton = $ '.download-btn'
|
||||
|
||||
@@ -58,8 +61,10 @@ $(document).ready ->
|
||||
#
|
||||
buildUrl = (width, height, foreground, background, name) ->
|
||||
url = "/api/name/#{width}/#{height}/#{background}/#{foreground}/#{name}"
|
||||
if customLabel != '' then url += "?label=#{customLabel}"
|
||||
return url
|
||||
qs = ""
|
||||
if customLabel != '' then qs += "label=#{customLabel}"
|
||||
if rotation != 0 then qs += "rotation=#{rotation}"
|
||||
return if qs == "" then url else url + "?" + qs
|
||||
|
||||
# Builds a URL to generate a molecule-only image from a compound name.
|
||||
#
|
||||
@@ -70,8 +75,10 @@ $(document).ready ->
|
||||
#
|
||||
buildMoleculeOnlyUrl = (width, height, foreground, name) ->
|
||||
url = "/api/name/#{width}/#{height}/#{foreground}/#{name}"
|
||||
if customLabel != '' then url += "?label=#{customLabel}"
|
||||
return url
|
||||
qs = ""
|
||||
if customLabel != '' then qs += "label=#{customLabel}"
|
||||
if rotation != 0 then qs += "rotation=#{rotation}"
|
||||
return if qs == "" then url else url + "?" + qs
|
||||
|
||||
# Builds a URL to generate a wallpaper image from a SMILES structure.
|
||||
#
|
||||
@@ -83,8 +90,10 @@ $(document).ready ->
|
||||
#
|
||||
buildSmilesUrl = (width, height, foreground, background, smiles) ->
|
||||
url = "/api/smiles/#{width}/#{height}/#{background}/#{foreground}/#{smiles}"
|
||||
if customLabel != '' then url += "?label=#{customLabel}"
|
||||
return url
|
||||
qs = ""
|
||||
if customLabel != '' then qs += "label=#{customLabel}"
|
||||
if rotation != 0 then qs += "rotation=#{rotation}"
|
||||
return if qs == "" then url else url + "?" + qs
|
||||
|
||||
# Builds a URL to generate a molecule-only image from a SMILES structure.
|
||||
#
|
||||
@@ -95,8 +104,10 @@ $(document).ready ->
|
||||
#
|
||||
buildSmilesMoleculeOnlyUrl = (width, height, foreground, smiles) ->
|
||||
url = "/api/smiles/#{width}/#{height}/#{foreground}/#{smiles}"
|
||||
if customLabel != '' then url += "?label=#{customLabel}"
|
||||
return url
|
||||
qs = ""
|
||||
if customLabel != '' then qs += "label=#{customLabel}"
|
||||
if rotation != 0 then qs += "rotation=#{rotation}"
|
||||
return if qs == "" then url else url + "?" + qs
|
||||
|
||||
# Checks if a compound name can be converted to SMILES using the configured database.
|
||||
#
|
||||
@@ -153,7 +164,8 @@ $(document).ready ->
|
||||
params = {
|
||||
'label': customLabel,
|
||||
'background': backgroundColor,
|
||||
'foreground': foregroundColor
|
||||
'foreground': foregroundColor,
|
||||
'rotation': rotation
|
||||
}
|
||||
if smilesMode
|
||||
params['smiles'] = currentCompoundSmiles
|
||||
@@ -180,16 +192,16 @@ $(document).ready ->
|
||||
refreshPreviewCompoundName = ->
|
||||
currentCompoundName = getCompoundName()
|
||||
smilesMode = false
|
||||
url = buildMoleculeOnlyUrl screenWidth, screenHeight, foregroundColor, currentCompoundName
|
||||
updatePreview previewElement, url, backgroundColor
|
||||
url = buildMoleculeOnlyUrl screenWidth, screenHeight, foregroundColor, currentCompoundName, rotation
|
||||
updatePreview previewElement, url, backgroundColor, rotation
|
||||
|
||||
# Refreshes the preview using the SMILES structure text box.
|
||||
#
|
||||
refreshPreviewSmiles = ->
|
||||
currentCompoundSmiles = getCompoundSmiles()
|
||||
smilesMode = true
|
||||
url = buildSmilesMoleculeOnlyUrl screenWidth, screenHeight, foregroundColor, currentCompoundSmiles
|
||||
updatePreview previewElement, url, backgroundColor
|
||||
url = buildSmilesMoleculeOnlyUrl screenWidth, screenHeight, foregroundColor, currentCompoundSmiles, rotation
|
||||
updatePreview previewElement, url, backgroundColor, rotation
|
||||
|
||||
# Refreshes the preview using the compound name or SMILES structure text box depending on mode.
|
||||
#
|
||||
@@ -205,7 +217,7 @@ $(document).ready ->
|
||||
backgroundColor = passedBackground
|
||||
|
||||
# Let's initialize the color pickers.
|
||||
|
||||
|
||||
$('.picker-fg').spectrum
|
||||
color: "##{foregroundColor}"
|
||||
clickoutFiresChange: true
|
||||
@@ -219,7 +231,7 @@ $(document).ready ->
|
||||
preferredFormat: 'hex'
|
||||
|
||||
# Set up color picker change events.
|
||||
|
||||
|
||||
$('.picker-fg').on 'change', (e) ->
|
||||
foregroundColor = $(e.target).val().substring(1)
|
||||
modeAwareRefreshPreview()
|
||||
@@ -227,9 +239,28 @@ $(document).ready ->
|
||||
$('.picker-bg').on 'change', (e) ->
|
||||
backgroundColor = $(e.target).val().substring(1)
|
||||
modeAwareRefreshPreview()
|
||||
|
||||
|
||||
# Set up the rotation knob.
|
||||
|
||||
rotationKnob = pureknob.createKnob(88, 88)
|
||||
rotationKnob.setProperty 'angleStart', 0
|
||||
rotationKnob.setProperty 'angleEnd', Math.PI * 2
|
||||
rotationKnob.setProperty 'colorFG', '#C0C0C0'
|
||||
rotationKnob.setProperty 'colorBG', '#505050'
|
||||
rotationKnob.setProperty 'trackWidth', 0.4
|
||||
rotationKnob.setProperty 'valMin', 0
|
||||
rotationKnob.setProperty 'valMax', 359
|
||||
rotationKnob.setProperty 'needle', true
|
||||
rotationKnob.setValue 0
|
||||
rotationKnob.addListener (knob, value) ->
|
||||
rotation = value
|
||||
modeAwareRefreshPreview()
|
||||
knobNode = rotationKnob.node()
|
||||
knobElem = document.getElementById 'rotation_knob'
|
||||
knobElem.appendChild knobNode
|
||||
|
||||
# Compound name update button should refresh the preview.
|
||||
|
||||
|
||||
$('.update-btn').on 'click', (e) ->
|
||||
errorRows.hide()
|
||||
checkMoleculeName getCompoundName(), refreshPreviewCompoundName, failPreview
|
||||
@@ -245,7 +276,7 @@ $(document).ready ->
|
||||
customLabel = getCustomLabel()
|
||||
modeAwareRefreshPreview()
|
||||
|
||||
# Put passed parameter values into text boxes if needed.
|
||||
# Put passed parameter values into UI if needed.
|
||||
|
||||
passedCompoundName = getParameterByName 'compound'
|
||||
if passedCompoundName != null
|
||||
@@ -261,11 +292,16 @@ $(document).ready ->
|
||||
if passedLabel != null
|
||||
customLabelTextBox.val(passedLabel)
|
||||
|
||||
# Grab initial values from text boxes.
|
||||
passedRotation = getParameterByName 'rotation'
|
||||
if passedRotation != null
|
||||
rotationKnob.setValue(passedRotation)
|
||||
|
||||
# Grab initial values from UI.
|
||||
|
||||
currentCompoundName = compoundTextBox.val()
|
||||
currentCompoundSmiles = smilesTextBox.val()
|
||||
customLabel = customLabelTextBox.val()
|
||||
rotation = rotationKnob.getValue()
|
||||
|
||||
# Initial update.
|
||||
modeAwareRefreshPreview()
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
@picker-border: #444;
|
||||
|
||||
// Width variables.
|
||||
@sidebar-width: 250px;
|
||||
@sidebar-width: 325px;
|
||||
@width2: 100px;
|
||||
@full-width: 100%;
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<script type="text/javascript" src="/bower_components/jquery/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/bower_components/spectrum/spectrum.js"></script>
|
||||
<script type="text/javascript" src="/js-lib/pureknob.js"></script>
|
||||
<script type="text/javascript" src="/js/params.js"></script>
|
||||
<script type="text/javascript" src="/js/hamburger.js"></script>
|
||||
<script type="text/javascript" src="/js/main.js"></script>
|
||||
@@ -110,24 +111,38 @@
|
||||
<div class="tool-section mtop-10">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="section-lbl">Colours</div>
|
||||
<div class="section-lbl">Colors and Rotation</div>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mtop-10">
|
||||
<div class="col-xs-6">
|
||||
<div class="picker-lbl">Molecule:</div>
|
||||
<div class="col-xs-7">
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="picker-lbl">Molecule:</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<input type="text" class="picker picker-fg">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="picker-lbl">Background:</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<input type="text" class="picker picker-bg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<input type="text" class="picker picker-fg">
|
||||
<div class="col-xs-5">
|
||||
<div id="rotation_knob"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-section mtop-10">
|
||||
<div class="row mtop-10">
|
||||
<div class="col-xs-6">
|
||||
<div class="picker-lbl">Background:</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<input type="text" class="picker picker-bg">
|
||||
<div id="rotation_knob"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+14
-2
@@ -14,7 +14,7 @@ use Avogadrio\MoleculeRenderer;
|
||||
$app = new Silex\Application();
|
||||
|
||||
// Uncomment the line below while debugging your app.
|
||||
// $app['debug'] = true;
|
||||
$app['debug'] = true;
|
||||
|
||||
// Load config.
|
||||
$config = Spyc::YAMLLoad(__DIR__ . '/../config/config.yaml');
|
||||
@@ -52,6 +52,9 @@ $app->get('/api/smiles/{width}/{height}/{background}/{foreground}/{smiles}',
|
||||
// Add label.
|
||||
$moleculeRenderer->setCustomLabel($request->get('label'));
|
||||
|
||||
// Add rotation.
|
||||
$moleculeRenderer->setRotation((float) $request->get('rotation'));
|
||||
|
||||
// Render molecule with background.
|
||||
$image = $moleculeRenderer->renderMoleculeWithBackground($smiles, $foreground, $background, $width, $height);
|
||||
|
||||
@@ -68,6 +71,9 @@ $app->get('/api/smiles/{width}/{height}/{color}/{smiles}',
|
||||
// Add label.
|
||||
$moleculeRenderer->setCustomLabel($request->get('label'));
|
||||
|
||||
// Add rotation.
|
||||
$moleculeRenderer->setRotation((float) $request->get('rotation'));
|
||||
|
||||
// Render molecule only.
|
||||
$image = $moleculeRenderer->renderScaledMolecule($smiles, $color, $width, $height);
|
||||
|
||||
@@ -80,7 +86,7 @@ $app->get('/api/smiles/{width}/{height}/{color}/{smiles}',
|
||||
*/
|
||||
$app->get('/api/name/{width}/{height}/{background}/{foreground}/{name}',
|
||||
function (Request $request, $width, $height, $background, $foreground, $name) use ($app, $moleculeRenderer, $smilesConverter) {
|
||||
|
||||
|
||||
// Convert chemical name to SMILES if we can.
|
||||
$smiles = $smilesConverter->nameToSmiles($name);
|
||||
|
||||
@@ -90,6 +96,9 @@ $app->get('/api/name/{width}/{height}/{background}/{foreground}/{name}',
|
||||
// Add label.
|
||||
$moleculeRenderer->setCustomLabel($request->get('label'));
|
||||
|
||||
// Add rotation.
|
||||
$moleculeRenderer->setRotation((float) $request->get('rotation'));
|
||||
|
||||
// Render molecule with background.
|
||||
$image = $moleculeRenderer->renderMoleculeWithBackground($smiles, $foreground, $background, $width, $height);
|
||||
|
||||
@@ -116,6 +125,9 @@ $app->get('/api/name/{width}/{height}/{color}/{name}',
|
||||
// Add label.
|
||||
$moleculeRenderer->setCustomLabel($request->get('label'));
|
||||
|
||||
// Add rotation.
|
||||
$moleculeRenderer->setRotation((float) $request->get('rotation'));
|
||||
|
||||
// Render molecule only.
|
||||
$image = $moleculeRenderer->renderScaledMolecule($smiles, $color, $width, $height);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user